Polars支持从嵌套字典创建数据框架吗?

4
我正在尝试从一个字典(mainDict)创建一个polars数据框,其中mainDict的一个值是一个包含字典对象(nestedDicts)的列表。当我尝试执行此操作时,会出现错误(见下文),我不知道其含义。但是,pandas确实允许我使用mainDict创建数据框。
我不确定自己是否做错了什么,这是否是一个bug,或者polars是否根本不支持这种操作。如果可能的话,我想用这种方式来实现它,但是我不太担心找到解决方法(欢迎提供建议)。
我在google colab上使用的是polars版本0.13.38(在VScode上也会出现问题),python版本为3.9.6,操作系统为windows 10。以下是重现问题及其输出的代码示例。谢谢!
输入:
import polars as pl
import pandas as pd

template = {    'a':['A', 'AA'],
                'b':['B', 'BB'],
                'c':['C', 'CC'],
                'd':[{'D1':'D2'}, {'DD1':'DD2'}]}

#create a dataframe using pandas
df_pandas = pd.DataFrame(template)
print(df_pandas)

#create a dataframe using polars
df_polars = pl.DataFrame(template)
print(df_polars)

输出:

    a   b   c               d
0   A   B   C    {'D1': 'D2'}
1  AA  BB  CC  {'DD1': 'DD2'}
---------------------------------------------------------------------------
ComputeError                              Traceback (most recent call last)
<ipython-input-9-2abdc86d91da> in <module>()
     12 
     13 #create a dataframe using polars
---> 14 df_polars = pl.DataFrame(template)
     15 print(df_polars)

3 frames
/usr/local/lib/python3.7/dist-packages/polars/internals/frame.py in __init__(self, data, columns, orient)
    300 
    301         elif isinstance(data, dict):
--> 302             self._df = dict_to_pydf(data, columns=columns)
    303 
    304         elif isinstance(data, np.ndarray):

/usr/local/lib/python3.7/dist-packages/polars/internals/construction.py in dict_to_pydf(data, columns)
    400         return PyDataFrame(data_series)
    401     # fast path
--> 402     return PyDataFrame.read_dict(data)
    403 
    404 

/usr/local/lib/python3.7/dist-packages/polars/internals/series.py in __init__(self, name, values, dtype, strict, nan_to_null)
    225                 self._s = self.cast(dtype, strict=True)._s
    226         elif isinstance(values, Sequence):
--> 227             self._s = sequence_to_pyseries(name, values, dtype=dtype, strict=strict)
    228         elif _PANDAS_AVAILABLE and isinstance(values, (pd.Series, pd.DatetimeIndex)):
    229             self._s = pandas_to_pyseries(name, values)

/usr/local/lib/python3.7/dist-packages/polars/internals/construction.py in sequence_to_pyseries(name, values, dtype, strict)
    241             if constructor == PySeries.new_object:
    242                 try:
--> 243                     return PySeries.new_from_anyvalues(name, values)
    244                 # raised if we cannot convert to Wrap<AnyValue>
    245                 except RuntimeError:

ComputeError: struct orders must remain the same
1个回答

5
您收到的错误是因为您的字典列表不符合 Polars 中 Series of struct 的期望。更具体地说,您的两个字典 {'D1':'D2'}{'DD1':'DD2'} 映射到 Polars 中的两种不同类型的结构体,因此不适用于包含在同一个Series中。
首先需要解释什么是结构体...
Polars:结构体
在 Polars 中,字典被映射到称为struct的东西。 结构体是一组有序,命名的类型数据集合。 (在这方面,结构体与只有一行的 Polars DataFrame非常相似。)
在结构体中:
每个字段必须有唯一的字段名称 每个字段都有一个数据类型 结构体中字段的顺序很重要
Polars: 将字典映射到结构体
当字典映射到结构体中(例如在DataFrame构造函数中),字典中的每个键都会映射到结构体中的字段名,并将相应的字典值分配给结构体中该字段的值。

此外,字典中键的顺序很重要:结构体的字段按照字典中键的顺序创建。在Python中,很容易忘记字典中的键是有序的。

自3.7版本起更改:保证字典顺序为插入顺序。这种行为是CPython从3.6开始的实现细节。

Polars:结构体的系列/列表

这就是Polars中出问题的地方。 仅当结构体集合满足以下条件时,才能将其包含在同一个Series中:

  1. 结构体具有相同数量的字段
  2. 字段具有相同的名称
  3. 字段按相同顺序排列
  4. 每个字段的数据类型对于每个结构体都相同。
在您的输入中,{'D1':'D2'}被映射到一个具有一个字段名为"D1"和值为"D2"的结构体中。然而,{'DD1':'DD2'}被映射到一个具有字段名为"DD1"和值为"DD2"的结构体中。因此,这些结果结构体不兼容并且不能包含在同一个Series中。它们的字段名不匹配。
在这种情况下,Polars比Pandas更加挑剔,后者允许任意键值对的字典出现在同一列中。
通常情况下,您会发现Polars对数据结构和数据类型的看法比Pandas更加强烈。(部分原因是性能相关的。)
解决方法
您的示例的一种解决方法是修改您的字典,使其包含相同的键并按相同顺序排列。例如:
template = {
    "a": ["A", "AA"],
    "b": ["B", "BB"],
    "c": ["C", "CC"],
    "d": [{"D1": "D2", "DD1": None}, {"D1": None, "DD1": "DD2"}],
}
pl.DataFrame(template)

shape: (2, 4)
┌─────┬─────┬─────┬──────────────┐
│ a   ┆ b   ┆ c   ┆ d            │
│ --- ┆ --- ┆ --- ┆ ---          │
│ str ┆ str ┆ str ┆ struct[2]    │
╞═════╪═════╪═════╪══════════════╡
│ A   ┆ B   ┆ C   ┆ {"D2",null}  │
├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ AA  ┆ BB  ┆ CC  ┆ {null,"DD2"} │
└─────┴─────┴─────┴──────────────┘

另一个简单的解决方法是先将数据导入Pandas,然后再将Pandas DataFrame导入Polars。导入过程会为您完成这项工作。
template = {
    "a": ["A", "AA"],
    "b": ["B", "BB"],
    "c": ["C", "CC"],
    "d": [{"D1": "D2"}, {"DD1": "DD2"}],
}
pl.DataFrame(pd.DataFrame(template))

>>> pl.DataFrame(pd.DataFrame(template))
shape: (2, 4)
┌─────┬─────┬─────┬──────────────┐
│ a   ┆ b   ┆ c   ┆ d            │
│ --- ┆ --- ┆ --- ┆ ---          │
│ str ┆ str ┆ str ┆ struct[2]    │
╞═════╪═════╪═════╪══════════════╡
│ A   ┆ B   ┆ C   ┆ {"D2",null}  │
├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ AA  ┆ BB  ┆ CC  ┆ {null,"DD2"} │
└─────┴─────┴─────┴──────────────┘

可能还有其他解决方法,但这将取决于您具体的数据和需求。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接