私は次のSO thead を読んで、それを理解しようとしています。ここに私の例があります:
import dask.dataframe as dd
import pandas as pd
from dask.multiprocessing import get
import random
df = pd.DataFrame({'col_1':random.sample(range(10000), 10000), 'col_2': random.sample(range(10000), 10000) })
def test_f(col_1, col_2):
return col_1*col_2
ddf = dd.from_pandas(df, npartitions=8)
ddf['result'] = ddf.map_partitions(test_f, columns=['col_1', 'col_2']).compute(get=get)
以下のエラーが発生します。何が悪いのですか?また、map_partitions
で機能する追加のパラメーターを渡す方法もわかりません。
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
~\AppData\Local\conda\conda\envs\tensorflow\lib\site-packages\dask\dataframe\utils.py in raise_on_meta_error(funcname)
136 try:
--> 137 yield
138 except Exception as e:
~\AppData\Local\conda\conda\envs\tensorflow\lib\site-packages\dask\dataframe\core.py in _emulate(func, *args, **kwargs)
3130 with raise_on_meta_error(funcname(func)):
-> 3131 return func(*_extract_meta(args, True), **_extract_meta(kwargs, True))
3132
TypeError: test_f() got an unexpected keyword argument 'columns'
During handling of the above exception, another exception occurred:
ValueError Traceback (most recent call last)
<ipython-input-9-913789c7326c> in <module>()
----> 1 ddf['result'] = ddf.map_partitions(test_f, columns=['col_1', 'col_2']).compute(get=get)
~\AppData\Local\conda\conda\envs\tensorflow\lib\site-packages\dask\dataframe\core.py in map_partitions(self, func, *args, **kwargs)
469 >>> ddf.map_partitions(func).clear_divisions() # doctest: +SKIP
470 """
--> 471 return map_partitions(func, self, *args, **kwargs)
472
473 @insert_meta_param_description(pad=12)
~\AppData\Local\conda\conda\envs\tensorflow\lib\site-packages\dask\dataframe\core.py in map_partitions(func, *args, **kwargs)
3163
3164 if meta is no_default:
-> 3165 meta = _emulate(func, *args, **kwargs)
3166
3167 if all(isinstance(arg, Scalar) for arg in args):
~\AppData\Local\conda\conda\envs\tensorflow\lib\site-packages\dask\dataframe\core.py in _emulate(func, *args, **kwargs)
3129 """
3130 with raise_on_meta_error(funcname(func)):
-> 3131 return func(*_extract_meta(args, True), **_extract_meta(kwargs, True))
3132
3133
~\AppData\Local\conda\conda\envs\tensorflow\lib\contextlib.py in __exit__(self, type, value, traceback)
75 value = type()
76 try:
---> 77 self.gen.throw(type, value, traceback)
78 except StopIteration as exc:
79 # Suppress StopIteration *unless* it's the same exception that
~\AppData\Local\conda\conda\envs\tensorflow\lib\site-packages\dask\dataframe\utils.py in raise_on_meta_error(funcname)
148 ).format(" in `{0}`".format(funcname) if funcname else "",
149 repr(e), tb)
--> 150 raise ValueError(msg)
151
152
ValueError: Metadata inference failed in `test_f`.
Original error is below:
------------------------
TypeError("test_f() got an unexpected keyword argument 'columns'",)
Traceback:
---------
File "C:\Users\some_user\AppData\Local\conda\conda\envs\tensorflow\lib\site-packages\dask\dataframe\utils.py", line 137, in raise_on_meta_error
yield
File "C:\Users\some_user\AppData\Local\conda\conda\envs\tensorflow\lib\site-packages\dask\dataframe\core.py", line 3131, in _emulate
return func(*_extract_meta(args, True), **_extract_meta(kwargs, True))
_map_partitions
_ docs に、実行しようとしていることを正確に実行する例があります。
_ddf.map_partitions(lambda df: df.assign(z=df.x * df.y))
_
_map_partitions
_を呼び出すとき(_pandas.DataFrame
_で.apply()
を呼び出すときと同じように)、map
(またはapply
)を試す関数最初の引数としてデータフレームが与えられます。
_dask.dataframe.map_partitions
_の場合、この最初の引数はapartitionであり、_pandas.DataFrame.apply
_の場合-データフレーム全体です。
つまり、関数は最初の引数としてdataframe(partition)を受け入れる必要があり、この場合は次のようになります。
_def test_f(df, col_1, col_2):
return df.assign(result=df[col_1] * df[col_2])
_
この場合の新しい列の割り当ては、.compute()
を呼び出す前に行われる(つまり、実行がスケジュールされる)ことに注意してください。
この例では、.compute()
を呼び出した後に列を割り当てますが、これはdaskを使用する目的に反します。つまり.compute()
を呼び出した後、その操作の結果がメモリにロードされますそれらの結果のための十分なスペースがある場合(そうでない場合、MemoryError
)。
したがって、例を機能させるには、次のようにします。
1)関数を使用(引数として列名を使用):
_def test_f(df, col_1, col_2):
return df.assign(result=df[col_1] * df[col_2])
ddf_out = ddf.map_partitions(test_f, 'col_1', 'col_2')
# Here is good place to do something with BIG ddf_out dataframe before calling .compute()
result = ddf_out.compute(get=get) # Will load the whole dataframe into memory
_
2)lambda
を使用します(関数に列名がハードコードされています):
_ddf_out = ddf.map_partitions(lambda df: df.assign(result=df.col_1 * df.col_2))
# Here is good place to do something with BIG ddf_out dataframe before calling .compute()
result = ddf_out.compute(get=get) # Will load the whole dataframe into memory
_
更新:
行ごとに関数を適用するために、リンクした投稿からの引用は次のとおりです。
map
/apply
map
を使用して、シリーズ全体で行方向に関数をマッピングできます。_df.mycolumn.map(func)
_
apply
を使用すると、データフレーム全体に関数を行ごとにマッピングできます。_df.apply(func, axis=1)
_
つまりあなたの質問の例の関数では、これは次のようになります:
_def test_f(dds, col_1, col_2):
return dds[col_1] * dds[col_2]
_
行ごとに適用するので、関数の最初の引数はシリーズになります(つまり、データフレームの各行はシリーズです)。
この関数を適用するには、次のように呼び出します。
_dds_out = ddf.apply(
test_f,
args=('col_1', 'col_2'),
axis=1,
meta=('result', int)
).compute(get=get)
_
これは、_'result'
_という名前のシリーズを返します。
関数を使用して各パーティションで_.apply
_を呼び出すこともできますが、データフレームで直接_.apply
_を呼び出すよりも効率的ではありません。しかし、あなたのテストはそうでなければ証明されるかもしれません。
君の test_f
は2つの引数を取ります:col_1
およびcol_2
。単一の引数ddf
を渡します。
のようなものを試してください
In [5]: dd.map_partitions(test_f, ddf['col_1'], ddf['col_2'])
Out[5]:
Dask Series Structure:
npartitions=8
0 int64
1250 ...
...
8750 ...
9999 ...
dtype: int64
Dask Name: test_f, 32 tasks