分类数据 (Categoricals)
分类数据 (Categoricals)¶
Dask DataFrame 将 分类数据 分为两种类型
已知分类数据(Known categoricals)是指其
categories
在静态时已知(在_meta
属性上)。每个分区**必须**具有与_meta
属性上找到的类别相同的类别。未知分类数据(Unknown categoricals)是指其类别在静态时未知,且每个分区可能包含不同的类别。在内部,未知分类数据由
_meta
属性的类别中存在dd.utils.UNKNOWN_CATEGORIES
表示。由于大多数 DataFrame 操作会传播类别,因此已知/未知状态应通过操作传播(类似于NaN
的传播方式)。
对于指定为描述(上述选项 2)的元数据,将创建未知分类数据。
某些操作仅适用于已知分类数据。例如,df.col.cat.categories
仅在 df.col
具有已知类别时才有效,因为分类映射仅在已知分类数据的元数据上是静态已知的。
分类列的已知/未知状态可以使用分类访问器上的 known
属性找到。
>>> ddf.col.cat.known
False
此外,可以使用 .cat.as_known()
将未知分类数据转换为已知分类数据。如果一个 DataFrame 中有多个分类列,您可能更希望使用 df.categorize(columns=...)
,它将所有指定的列转换为已知分类数据。由于获取类别需要完整扫描数据,使用 df.categorize()
比为每个列调用 .cat.as_known()
更高效(后者会导致多次扫描)。
>>> col_known = ddf.col.cat.as_known() # use for single column
>>> col_known.cat.known
True
>>> ddf_known = ddf.categorize() # use for multiple columns
>>> ddf_known.col.cat.known
True
要将已知分类数据转换为未知分类数据,也有 .cat.as_unknown()
方法。这不需要计算,因为它只是元数据的更改。
非分类列可以通过几种不同的方式转换为分类数据。
# astype operates lazily, and results in unknown categoricals
ddf = ddf.astype({'mycol': 'category', ...})
# or
ddf['mycol'] = ddf.mycol.astype('category')
# categorize requires computation, and results in known categoricals
ddf = ddf.categorize(columns=['mycol', ...])
此外,从 Pandas 0.19.2 及更高版本开始,dd.read_csv
和 dd.read_table
可以通过将列 dtype 指定为 'category'
直接读取数据到未知分类列。
>>> ddf = dd.read_csv(..., dtype={col_name: 'category'})
而且,从 Pandas 0.21.0 及更高版本开始,dd.read_csv
和 dd.read_table
可以通过指定 pd.api.types.CategoricalDtype
的实例直接读取数据到*已知*分类数据。
>>> dtype = {'col': pd.api.types.CategoricalDtype(['a', 'b', 'c'])}
>>> ddf = dd.read_csv(..., dtype=dtype)
如果您写入和读取 parquet 文件,Dask 将会忘记已知类别。发生这种情况是因为,出于性能考虑,所有类别都保存在每个分区中,而不是保存在 parquet 元数据中。可以手动加载类别。
>>> import dask.dataframe as dd
>>> import pandas as pd
>>> df = pd.DataFrame(data=list('abcaabbcc'), columns=['col'])
>>> df.col = df.col.astype('category')
>>> ddf = dd.from_pandas(df, npartitions=1)
>>> ddf.col.cat.known
True
>>> ddf.to_parquet('tmp')
>>> ddf2 = dd.read_parquet('tmp')
>>> ddf2.col.cat.known
False
>>> ddf2.col = ddf2.col.cat.set_categories(ddf2.col.head(1).cat.categories)
>>> ddf2.col.cat.known
True