dask 数据框应用元

dask dataframe apply meta

我想对 dask 数据帧的单列进行频率计数。该代码有效,但我收到 warning 抱怨未定义 meta。如果我尝试定义 meta,我会得到一个错误 AttributeError: 'DataFrame' object has no attribute 'name'。对于这个特定的用例,我似乎不需要定义 meta,但我想知道如何定义以供将来参考。

虚拟数据框和列频率

import pandas as pd
from dask import dataframe as dd

df = pd.DataFrame([['Sam', 'Alex', 'David', 'Sarah', 'Alice', 'Sam', 'Anna'],
                   ['Sam', 'David', 'David', 'Alice', 'Sam', 'Alice', 'Sam'],
                   [12, 10, 15, 23, 18, 20, 26]],
                  index=['Column A', 'Column B', 'Column C']).T
dask_df = dd.from_pandas(df)

In [39]: dask_df.head()
Out[39]: 
  Column A Column B Column C
0      Sam      Sam       12
1     Alex    David       10
2    David    David       15
3    Sarah    Alice       23
4    Alice      Sam       18

(dask_df.groupby('Column B')
        .apply(lambda group: len(group))
       ).compute()

UserWarning: `meta` is not specified, inferred from partial data. Please provide `meta` if the result is unexpected.
  Before: .apply(func)
  After:  .apply(func, meta={'x': 'f8', 'y': 'f8'}) for dataframe result
  or:     .apply(func, meta=('x', 'f8'))            for series result
  warnings.warn(msg)
Out[60]: 
Column B
Alice    2
David    2
Sam      3
dtype: int64

试图定义 meta 产生 AttributeError

 (dask_df.groupby('Column B')
         .apply(lambda d: len(d), meta={'Column B': 'int'})).compute()

这个也一样

 (dask_df.groupby('Column B')
         .apply(lambda d: len(d), meta=pd.DataFrame({'Column B': 'int'}))).compute()

如果我尝试让 dtype 成为 int 而不是 "int" 或就此而言 'f8'np.float64,那么它似乎并不好像是 dtype 导致了问题。

meta 上的文档似乎暗示我应该做我想做的事情 (http://dask.pydata.org/en/latest/dataframe-design.html#metadata)。

什么是meta?我该如何定义它?

使用 python 3.6 dask 0.14.3pandas 0.20.2

meta是计算输出的names/types的规定。这是必需的,因为 apply() 足够灵活,它可以从数据帧中生成几乎任何东西。如您所见,如果您不提供 meta,那么 dask 实际上会计算部分数据,以查看类型应该是什么——这很好,但您应该知道它正在发生。 您可以避免这种预先计算(这可能很昂贵),并且在您知道输出应该是什么样子时更加明确,方法是提供输出的零行版本(数据帧或系列),或仅提供类型。

你的计算输出实际上是一个系列,所以下面是最简单的工作

(dask_df.groupby('Column B')
     .apply(len, meta=('int'))).compute()

但更准确的是

(dask_df.groupby('Column B')
     .apply(len, meta=pd.Series(dtype='int', name='Column B')))