我注意到,分配给pandas
DataFrame
列(使用.loc
索引器)的行为不同,具体取决于DataFrame
中存在的其他列作业的确切形式。使用三个示例DataFrame
:
df1 = pandas.DataFrame({
'col1': [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
})
# col1
# 0 [1, 2, 3]
# 1 [4, 5, 6]
# 2 [7, 8, 9]
df2 = pandas.DataFrame({
'col1': [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
'col2': [[10, 20, 30], [40, 50, 60], [70, 80, 90]]
})
# col1 col2
# 0 [1, 2, 3] [10, 20, 30]
# 1 [4, 5, 6] [40, 50, 60]
# 2 [7, 8, 9] [70, 80, 90]
df3 = pandas.DataFrame({
'col1': [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
'col2': [1, 2, 3]
})
# col1 col2
# 0 [1, 2, 3] 1
# 1 [4, 5, 6] 2
# 2 [7, 8, 9] 3
x = numpy.array([[111, 222, 333],
[444, 555, 666],
[777, 888, 999]])
我发现了以下内容:
df1
:
df1.col1 = x
结果:
df1
# col1
# 0 111
# 1 444
# 2 777
df1.loc[:, 'col1'] = x
结果:
df1
# col1
# 0 111
# 1 444
# 2 777
df1.loc[0:2, 'col1'] = x
结果:
# […]
# ValueError: could not broadcast input array from shape (3,3) into shape (3)
df2
:
df2.col1 = x
结果:
df2
# col1 col2
# 0 111 [10, 20, 30]
# 1 444 [40, 50, 60]
# 2 777 [70, 80, 90]
df2.loc[:, 'col1'] = x
结果:
df2
# col1 col2
# 0 111 [10, 20, 30]
# 1 444 [40, 50, 60]
# 2 777 [70, 80, 90]
df2.loc[0:2, 'col1'] = x
结果:
# […]
# ValueError: could not broadcast input array from shape (3,3) into shape (3)
df3
:
df3.col1 = x
结果:
df3
# col1 col2
# 0 111 1
# 1 444 2
# 2 777 3
df3.loc[:, 'col1'] = x
结果:
# ValueError: Must have equal len keys and value when setting with an ndarray
df3.loc[0:2, 'col1'] = x
结果:
# ValueError: Must have equal len keys and value when setting with an ndarray
因此,如果df.loc
中的其他列之一不具有dtype DataFrame
,似乎object
的行为似乎有所不同。
我的问题是:
ValueError
的情况下,为什么DataFrame
列中填充{{1的 first column }}数组? 注意:我对讨论以这种方式将列分配给numpy
数组是否有意义没有兴趣。我只想知道行为上的差异,以及这是否可以视为错误。
答案 0 :(得分:10)
为什么其他列会在这种情况下有所作为 任务?
简单的答案是因为Pandas检查数据帧内的混合类型。您可以使用源代码中使用的相同方法自行检查:
print(df1._is_mixed_type) # False
print(df2._is_mixed_type) # False
print(df3._is_mixed_type) # True
所使用的逻辑根据_is_mixed_type
的值而不同。具体来说,当您提供的输入的_setitem_with_indexer
为_is_mixed_type
时,True
中的以下测试将失败:
if len(labels) != value.shape[1]:
raise ValueError('Must have equal len keys and value '
'when setting with an ndarray')
换句话说,数组中的列多于数据框中要分配的列。
这是一个错误吗?我认为,在Pandas数据框中使用列表或数组会充满危险。 1 添加了ValueError
检查以解决更重要的问题(GH 7551)。
为什么不同版本的作业不相同?
通过df3['col1'] = x
进行分配的原因是col1
是现有系列。尝试使用df3['col3'] = x
,您的代码将失败,并显示ValueError
。
深入挖掘一下,对于__setitem__
是语法糖的datframe的df[]
方法,将'col1'
标签通过key = com._apply_if_callable(key, self)
转换为一系列(如果存在) :
def _apply_if_callable(maybe_callable, obj, **kwargs):
"""
Evaluate possibly callable input using obj and kwargs if it is callable,
otherwise return as it is
"""
if callable(maybe_callable):
return maybe_callable(obj, **kwargs)
return maybe_callable
然后,该逻辑可以避开_setitem_with_indexer
中的检查逻辑。您可以推断出这一点,因为当我们为现有系列提供标签时,我们跳至_setitem_array
,而不是_set_item
:
def __setitem__(self, key, value):
key = com._apply_if_callable(key, self)
if isinstance(key, (Series, np.ndarray, list, Index)):
self._setitem_array(key, value)
elif isinstance(key, DataFrame):
self._setitem_frame(key, value)
else:
self._set_item(key, value)
以上均为实施细节;您不应基于这些基本方法来使用Pandas语法,因为它们可能会不断变化。
1 我要说的是,默认情况下应将其禁用 ,而只能通过设置启用。这是存储和处理数据的极其低效的方式。有时它提供短期的便利,但是却以混乱的代码为代价。
答案 1 :(得分:5)
首先,让我尝试一下@jpp解释的技术性和严格性较低的版本。一般而言,当您尝试将numpy数组插入熊猫数据帧时,熊猫希望它们具有相同的等级和尺寸(例如,两者均为4x2,尽管如果numpy数组的等级低于熊猫也可以)例如,如果pandas尺寸为4x2,而numpy尺寸为4x1或2x1,则可以使用numpy broadcasting以获得更多信息)。
前面的观点很简单,当您尝试将3x3的numpy数组放入长度为3(基本上为3x1)的pandas列中时,pandas实际上没有标准的处理方式,并且不一致的行为是仅仅是结果。如果大熊猫总是提出一个例外可能会更好,但是一般来讲大熊猫会尝试做某事,但这可能没什么用。
第二,(从长远来看,我(这不是一个字面的答案))我可以保证,如果您不花很多时间解决二维填塞的繁琐细节,您的情况就会好得多。排列成单个熊猫列。取而代之,只需遵循如下所示的更典型的pandas方法,该方法将产生以下代码:(1)行为更可预测,(2)可读性更强,(3)运行快得多。
x = np.arange(1,10).reshape(3,3)
y = x * 10
z = x * 100
df = pd.DataFrame( np.hstack((x,y)), columns=['x1 x2 x3 y1 y2 y3'.split()] )
# x1 x2 x3 y1 y2 y3
# 0 1 2 3 10 20 30
# 1 4 5 6 40 50 60
# 2 7 8 9 70 80 90
df.loc[:,'x1':'x3'] = z
# x1 x2 x3 y1 y2 y3
# 0 100 200 300 10 20 30
# 1 400 500 600 40 50 60
# 2 700 800 900 70 80 90
我把它作为一个简单的索引,但是看起来您想要做的是建立一个更高层次的结构,而熊猫可以通过MultiIndex的功能在此提供帮助。在这种情况下,结果是语法更简洁,但请注意,在其他情况下使用起来可能更复杂(不值得在此处进行详细介绍):
df = pd.DataFrame( np.hstack((x,y)),
columns=pd.MultiIndex.from_product( [list('xy'),list('123')] ) )
df.loc[:,'x'] = z # now you can replace 'x1':'x3' with 'x'
您可能知道这一点,但是从数据帧中提取numpy数组也非常容易,因此您只要将numpy数组放入多列中就不会丢失任何内容。例如,在多索引的情况下:
df.loc[:,'x'].values
# array([[100, 200, 300],
# [400, 500, 600],
# [700, 800, 900]])