Polars 是否支持从嵌套字典创建数据框?

Does Polars support creating a dataframe from a nested dictionary?

我正在尝试从字典 (mainDict) 创建一个 polars 数据框,其中 mainDict 的值之一是字典对象列表 (nestedDicts)。当我尝试这样做时,我收到一个我不知道其含义的错误(见下文)。但是,pandas 确实允许我使用 mainDict 创建数据框。

我不确定我是否做错了什么,是否是一个错误,或者这个操作是否只是不受 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

您收到的错误是因为您的词典列表不符合 Polars 中 structSeries 的预期。更具体地说,您的两个词典 {'D1':'D2'}{'DD1':'DD2'} 被映射到 Polars 中的两种不同类型的结构,因此不兼容包含在同一个 Series.

我首先需要解释结构...

极地:结构

在 Polars 中,字典被映射到一个叫做 struct 的东西。结构是 有序命名 类型化数据的集合。 (在这方面,结构很像只有一行的 Polars DataFrame。)

在结构中:

  1. 每个字段必须有一个唯一的字段名称
  2. 每个字段都有一个数据类型
  3. 结构中字段的顺序很重要

Polars:将字典映射到结构

当字典映射到结构时(例如,在 DataFrame 构造函数中),字典中的每个键都映射到结构中的字段名称,并且相应的字典值被分配给该字段的值结构中的字段。

此外,字典中键的顺序很重要struct 字段的创建顺序与字典中键的顺序相同。在 Python 中,很容易忘记字典中的键是有序的。

Changed in version 3.7: Dictionary order is guaranteed to be insertion order. This behavior was an implementation detail of CPython from 3.6.

极地:Series/Lists 个结构

这是您的输入在 Polars 中遇到问题的地方。 只有在以下情况下,结构集合才能包含在同一个 Series 中:

  1. 结构体的字段数相同
  2. 字段名称相同
  3. 字段顺序相同
  4. 每个结构的每个字段的数据类型都相同。

在您的输入中,{'D1':'D2'} 映射到一个结构,其中一个字段的字段名称为“D1”,值为“D2”。但是,{'DD1':'DD2'} 映射到一个结构,其中一个字段的字段名称为“DD1”,值为“DD2”。因此,生成的结构不兼容以包含在相同的 Series 中。他们的字段名称不匹配。

在这种情况下,Polars far 比 Pandas 更挑剔,这允许具有任意 key-value 对的词典出现在同一列中。

一般来说,您会发现 Polars 对数据结构和数据类型的看法远比 Pandas 强。 (部分原因是 performance-related。)

解决方法

您的示例的一个解决方法是更改​​您的词典,以便它们以相同的顺序包含相同的键。例如:

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"} │
└─────┴─────┴─────┴──────────────┘

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