【发布时间】:2020-10-14 00:15:16
【问题描述】:
问题
如何不在 Pandas DataFrame 中将包含嵌套不规则数组的列拆分为新列时触发VisibleDeprecationWarning? 普遍接受和直截了当的方式,或者解释为什么现在不可能。
术语:
-
这篇文章中的数组指的是
numpy.array。 - Ragged 表示“类似列表的对象的集合”(即 list[list]、array[array]、list[tuple] 等)中的项目具有不相等数量的元素。
- 本文中的“嵌套参差不齐的数组”意味着数组在最深的层次上参差不齐,但在最外层的两个层次中元素的数量分别相等。换句话说,它可以转换成一个二维数组,其中包含可能不等长的列表对象。
现有职位调查
经过广泛的调查以及我自己进行的实验,我找不到一种普遍接受且直接的方法。下面列出了发布时关于 SO 的两个最相关的帖子。
这些帖子充其量与问题关系不大:post1、post2、post3、post4。
实验
样本 sata 和预期输出
df = pd.DataFrame(
data={
"id": ['a', 'b', 'c'],
"col1": [[[1, 2], [3, 4, 5]],
[[6], [7, 8, 9]],
[[10, 11, 12], []]
]
}
)
df
Out[81]:
id col1
0 a [[1, 2], [3, 4, 5]]
1 b [[6], [7, 8, 9]]
2 c [[10, 11, 12], []]
可以看到df["col1"] 在最外层的两个级别中具有 shape=(3, 2)。预期输出:
df # expected output
Out[177]:
id col1 sep1 sep2
0 a [[1, 2], [3, 4, 5]] [1, 2] [3, 4, 5]
1 b [[6], [7, 8, 9]] [6] [7, 8, 9]
2 c [[10, 11, 12], []] [10, 11, 12] []
为了节省时间,可以直接跳到最后一小节开始工作方法。下面按时间顺序介绍了我尝试过的所有相关策略。
主要试验
这里的分割函数产生了一个二元组的pd.Series,这是合理的。
df["col1"].apply(lambda el: (el[0], el[1]))
Out[82]:
0 ([1, 2], [3, 4, 5])
1 ([6], [7, 8, 9])
2 ([10, 11, 12], [])
Name: col1, dtype: object
但是,直接分配到单独的列会产生ValueError。
df[["sep1", "sep2"]] = df["col1"].apply(lambda el: (el[0], el[1]))
Traceback (most recent call last):
File "/opt/anaconda3/lib/python3.7/site-packages/IPython/core/interactiveshell.py", line 3417, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-75-973c44fe294a>", line 1, in <module>
df[["sep1", "sep2"]] = df["col1"].apply(lambda el: (el[0], el[1]))
File "/opt/anaconda3/lib/python3.7/site-packages/pandas/core/frame.py", line 3037, in __setitem__
self._setitem_array(key, value)
File "/opt/anaconda3/lib/python3.7/site-packages/pandas/core/frame.py", line 3072, in _setitem_array
self.iloc._setitem_with_indexer((slice(None), indexer), value)
File "/opt/anaconda3/lib/python3.7/site-packages/pandas/core/indexing.py", line 1755, in _setitem_with_indexer
"Must have equal len keys and value "
ValueError: Must have equal len keys and value when setting with an iterable
这可以通过使用.tolist() 将Series 转换为list 来避免。
df["col1"].apply(lambda el: (el[0], el[1])).tolist()
Out[84]: [([1, 2], [3, 4, 5]), ([6], [7, 8, 9]), ([10, 11, 12], [])]
现在直接分配可以正常工作,但会弹出一个VisibleDeprecationWarning。
df[["sep1", "sep2"]] = df["col1"].apply(lambda el: (el[0], el[1])).tolist()
/opt/anaconda3/lib/python3.7/site-packages/numpy/core/_asarray.py:83: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray
return array(a, dtype, copy=False, order=order)
df # this is expected
Out[86]:
id col1 sep1 sep2
0 a [[1, 2], [3, 4, 5]] [1, 2] [3, 4, 5]
1 b [[6], [7, 8, 9]] [6] [7, 8, 9]
2 c [[10, 11, 12], []] [10, 11, 12] []
list-zip-star 方法
ValueError 或 VisibleDeprecationWarning。
ls = df["col1"].apply(lambda el: (el[0], el[1])).tolist()
unpacked = list(zip(*ls))
df[["sep1", "sep2"]] = unpacked
# same ValueError message as above
df["sep1"] = unpacked[0]
# same VisibleDeprecationWarning message as above
list-map-list-zip-star 方法(有效但...)
只需随机添加另一层list-map。这一次,终于可以得到想要的输出了。但这在以下方面非常违反直觉:
- 必须单独分配新列。为什么不能一次完成?
- list-map-list-zip-star 功能非常令人费解。
我真的应该按设计这样做吗?
ls = df["col1"].apply(lambda el: (el[0], el[1])).tolist()
unpacked = list(map(list, zip(*ls))) # a magical spell
df[["sep1", "sep2"]] = unpacked
# same ValueError message. Why?
# set the new columns individually.
df["sep1"] = unpacked[0]
df["sep2"] = unpacked[1]
df # expected output
Out[177]:
id col1 sep1 sep2
0 a [[1, 2], [3, 4, 5]] [1, 2] [3, 4, 5]
1 b [[6], [7, 8, 9]] [6] [7, 8, 9]
2 c [[10, 11, 12], []] [10, 11, 12] []
【问题讨论】: