日々精進

新しく学んだことを書き留めていきます

DataFrameの処理を並列化すると非常に遅くなることがある

例えば、以下のようにグループ毎に集計する処理の場合

sums: DataFrame = train.groupby(ids)[SALES_COUNT].sum()

以下のようにするとグループ単位で並列化できるので速くなりそうに思える。

def func(df):
    return df[SALES_COUNT].sum()
ret_list: List = Parallel(n_jobs=cpu_count, backend="threading")(delayed(func)(group) for name, group in df.groupby(group_id))

が、実際計測してみると並列化しない場合は60秒、並列化した場合が3300秒と60倍近く並列化しない方が速かった。 想像だが、原因は以下かも。。

  • pythonの並列処理はマルチプロセスであり、各プロセスにデータのコピーを配るので余計時間がかかった
  • 並列化後のコードはapplyのようにグループ毎に関数を実行していくのでapplyが遅いのと同じ理由で遅くなったのかも

本当のところは分からないが、こんなに差が付くとは。。pythonムズカシイ

dockerでmountしたフォルダにwrite権限が無い場合の挙動

docker composeファイルで以下のようにホスト側のフォルダをmountしている場合。

    volumes:
      - "/var/log/app:/home/my-user/app/log"
  • /home/my-user/app/log(コンテナ側のフォルダ)にwrite権限がないユーザでファイルを保存しようとすると、Permission Deniedエラーが発生する。
  • /var/log/app(ホスト側のフォルダ)にwrite権限がないユーザでファイルを保存しようとすると、エラーは発生しないがホスト側にファイルが出来ない。コンテナ側のフォルダにはファイルができる。

DataFrame.locで複数列を一度に代入できない

以下のようなコードで、あるDataFrameの4つの列に別のDataFrameの値を代入しようとしたが、できなかった(代入してもsalesの値がNaNのまま)

sales.loc[mask, [SALES_COUNT_MA14, SALES_COUNT_MMAX14, SALES_COUNT_MMIN14, SALES_COUNT_MS14, SALES_COUNT_MM14]] = merged.iloc[:, 2:]

以下のように1列のみが対象なら代入できる。

sales.loc[mask, SALES_COUNT_MA14] = merged.iloc[:, 2]

以下のように.locの代わりに[]を使って代入先の列を指定すると代入できる。

sales = sales[mask]
sales[[SALES_COUNT_MA14, SALES_COUNT_MMAX14, SALES_COUNT_MMIN14, SALES_COUNT_MS14, SALES_COUNT_MM14]] = merged.iloc[:, 2:]

謎な仕様だな。。使い方が間違ってるのかな。

Windows10を初期化する方法

Windows10には初期化機能があり、簡単に初期化できる。詳細は以下参照。

www.comshop.co.jp

こんなに簡単に初期化できるとは。。便利。

ただ、初期化だけだと初期化前にあったファイルを復元できてしまう可能性があるので、他の人にPCを譲る場合は初期化後にファイルを完全消去するソフトを使う必要がある。

docker runを実行すると「ERROR: for Cannot start service : OCI runtime create failed: container_linux.go:348: starting container process caused "process_linux.go:402: container init caused \"process_linux.go:385: running prestart hook 1 caused \\\"error running h

原因はGPUの無いマシンでdocker-compose.yamlに runtime:nvidia と書いた状態でコンテナを起動しようとしたことだった。 runtime:nvidia を削除すると直った。

localeについて

localeとは言語、単位、時刻など国毎に異なる設定をまとめたもの。 C localeはデフォルトのlocale。基本アメリカに合わせた設定になるはず。 LANG, LC_TYPE, LC_ALLなどいくつかの環境変数を使って設定する。 en_US.UTF-8のように 言語_国.エンコーディング方式 の形で指定する。

eng-entrance.com

qiita.com

上記によると、localeはlibcの機能。つまり、C言語の標準関数を使ってメッセージを表示する時に、 標準関数が内部でLANG等の環境変数を参照して表示する言語を切り替えたりフォーマットを変えたりしている。 CPythonなどはC言語で実装されているのでlocaleの影響を受ける。もしCの標準関数を使わずにメッセージを表示しているところがあればそれはlocaleの影響を受けない。

参考:

qiita.com

PythonでPath.mkdirを実行すると「UnicodeEncodeError: 'ascii' codec can't encode character」エラー

原因はmkdirの中でOSのデフォルトエンコーディングに従ってmkdirに渡したpath文字列をエンコードしているため。 CPython実装を見ると、Py_FileSystemDefaultEncodingに従ってエンコードしているように見える。

cpython: 54c93e0fe79b Modules/posixmodule.c

Py_FileSystemDefaultEncodingは以下によるとLC_CTYPE依存。ここが正しく設定されてないのでPy_FileSystemDefaultEncodingがasciiになっており、os.mkdirの中でencodeに失敗していると思われる。

qiita.com

LC_CTYPEをen_US.UTF-8に設定するとPy_FileSystemDefaultEncodingがutf8になり、直った。