为什么用[:]与iloc [:]分配在熊猫中会产生不同的结果?


13

我对iloc在熊猫中使用不同的索引方法感到困惑。

假设我正在尝试将1维数据帧转换为2维数据帧。首先,我有以下一维数据框

a_array = [1,2,3,4,5,6,7,8]
a_df = pd.DataFrame(a_array).T

我将其转换为大小为的二维数据帧2x4。我首先将二维数据帧预设如下:

b_df = pd.DataFrame(columns=range(4),index=range(2))

然后我使用for循环通过以下代码帮助我将a_df(1-d)转换为b_df(2-d)

for i in range(2):
    b_df.iloc[i,:] = a_df.iloc[0,i*4:(i+1)*4]

它只给我以下结果

     0    1    2    3
0    1    2    3    4
1  NaN  NaN  NaN  NaN

但是当我改变b_df.iloc[i,:]b_df.iloc[i][:]。结果是正确的,如下所示,这是我想要的

   0  1  2  3
0  1  2  3  4
1  5  6  7  8

谁能向我解释.iloc[i,:]和之间的区别.iloc[i][:]是什么,为什么.iloc[i][:]在上面的示例中起作用,但没有.iloc[i,:]


这很好奇。b_df.iloc[1] = a_df.iloc[0, 4:8]将具有索引[4, 5, 6, 7]的序列分配给具有索引的序列[0, 1, 2, 3]。没有重叠,因此将NaNs分配给所有元素。到目前为止,这对我来说很有意义。但是,像您一样,我不清楚为什么b_df.iloc[1][:] = ...行为会有所不同-检查对象b_df.iloc[1]b_df.iloc[1][:]发现索引之间没有差异。我最好的猜测是,直接分配给副本([:])被Pandas视为特殊情况,这使其忽略了受让人的索引并造成了这种差异。
勒布

我认为这是因为有了索引,并且第一行成功是因为它具有相同的索引
Phung Duy Phong

1
关于熊猫,我要记住的关键一点是,熊猫中的大多数操作都使用一种称为“本机数据对齐”的概念。这意味着您对熊猫所做的几乎所有操作都会使语句两侧的索引对齐。在这里,您尝试使用索引0设置索引1,pandas将分配nans,因为该分配的右侧没有索引0。还请记住,列标题也是索引。因此,pandas会将列标题与列标题对齐。
斯科特·波士顿,

3
其次,使用.iloc [i] [:]称为索引链接,在熊猫中通常是很大的“禁止”。熊猫在创建对象的视图或在内存中创建全新的对象时会产生一些意外的后果。
斯科特·波士顿,

请不要忘记投票所有有效的答案,并接受最喜欢的答案。也许您知道这一点,但这是为了让社区知道哪些答案是有用的,并且也要奖励人们的时间和精力;)请参阅此meta.stackexchange.com/questions/5234/和meta.stackexchange.com/问题/ 173399 /
alan.elkin

Answers:


3

分配回来时,series.iloc[:]和之间有非常非常大的区别series[:](i)loc始终检查以确保您要分配的内容与受让人的索引匹配。同时,该[:]语法绕过索引对齐,分配给基础的NumPy数组。

s = pd.Series(index=[0, 1, 2, 3], dtype='float')  
s                                                                          

0   NaN
1   NaN
2   NaN
3   NaN
dtype: float64

# Let's get a reference to the underlying array with `copy=False`
arr = s.to_numpy(copy=False) 
arr 
# array([nan, nan, nan, nan])

# Reassign using slicing syntax
s[:] = pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'])                 
s                                                                          

0    1
1    2
2    3
3    4
dtype: int64

arr 
# array([1., 2., 3., 4.]) # underlying array has changed

# Now, reassign again with `iloc`
s.iloc[:] = pd.Series([5, 6, 7, 8], index=[3, 4, 5, 6]) 
s                                                                          

0    NaN
1    NaN
2    NaN
3    5.0
dtype: float64

arr 
# array([1., 2., 3., 4.])  # `iloc` created a new array for the series
                           # during reassignment leaving this unchanged

s.to_numpy(copy=False)     # the new underlying array, for reference                                                   
# array([nan, nan, nan,  5.]) 

现在您已经了解了它们之间的区别,让我们看看代码中会发生什么。只需打印出循环的RHS即可查看分配的内容:

for i in range(2): 
    print(a_df.iloc[0, i*4:(i+1)*4]) 

# output - first row                                                                   
0    1
1    2
2    3
3    4
Name: 0, dtype: int64
# second row. Notice the index is different
4    5
5    6
6    7
7    8
Name: 0, dtype: int64   

b_df.iloc[i, :]在第二次迭代中分配给时,索引是不同的,因此未分配任何内容,您只会看到NaN。但是,更改 b_df.iloc[i, :]b_df.iloc[i][:]意味着您将分配给基础的NumPy数组,因此将绕过索引对齐。此操作最好表示为

for i in range(2):
    b_df.iloc[i, :] = a_df.iloc[0, i*4:(i+1)*4].to_numpy()

b_df                                                                       

   0  1  2  3
0  1  2  3  4
1  5  6  7  8

还值得一提的是,这是链式分配的一种形式,这不是一件好事,并且还会使您的代码更难以阅读和理解。


1
现在我明白了,谢谢。在授予赏金之前,您可以为此添加一个参考:“ [:]语法分配给基础NumPy数组”吗?
勒布

@Seb在文档中找不到真正的引用,因为它有点实现细节。在GitHub上找到负责此工作的代码可能会更容易,但是我认为最简单的方法就是演示发生的情况。我已经编辑了答案顶部的小示例,以显示在不同类型的重新分配过程中如何操纵底层数组。希望事情变得更清楚!
cs95

非常感谢!现在要清楚得多了。
叶振棠

0

区别在于,在第一种情况下,Python解释器将代码执行为:

b_df.iloc[i,:] = a_df.iloc[0,i*4:(i+1)*4]
#as
b_df.iloc.__setitem__((i, slice(None)), value)

该值将在等式的右侧。在第二种情况下,Python解释器将代码执行为:

b_df.iloc[i][:] = a_df.iloc[0,i*4:(i+1)*4]
#as
b_df.iloc.__getitem__(i).__setitem__(slice(None), value)

该值再次位于等式的右侧。

在这两种情况的每种情况下,由于键(i,slice(None))和slice(None)的不同,将在内部setitem中调用不同的方法。因此,我们有不同的行为。


b_df.iloc[i]并且b_df.iloc[i][:]具有相同的索引。为什么可以将一个索引不匹配的序列分配给一个而不分配另一个序列?
勒布

在第一种情况下,将调用_set_item,在第二种情况下将调用。因此,怀疑由于这些方法的不同,我们具有上述行为
MaPy

0

谁能向我解释.iloc[i,:]和 之间的区别.iloc[i][:]是什么

.iloc[i,:]和之间的区别.iloc[i][:]

如果.iloc[i,:]您要直接访问的特定位置DataFrame,请选择第th行的所有(:)列i。据我所知,等于不指定第二维(.iloc[i])。

如果.iloc[i][:]您要执行2个链接的操作。因此,的结果.iloc[i]将受到的影响[:]。使用这组值由熊猫本身气馁这里有一个警告,所以你不应该使用它:

是否返回副本或参考以进行设置操作,可能取决于上下文。有时称为链式分配,应避免


...以及为什么.iloc[i][:]在上面的示例中有效,但没有.iloc[i,:]

正如在OP注释中提到的@Scott一样,数据对齐是固有的,因此=如果左侧没有索引,则不会包含右侧的索引。这就是NaN第二行上有值的原因。

因此,为清楚起见,您可以执行以下操作:

for i in range(2):
    # Get the slice
    a_slice = a_df.iloc[0, i*4:(i+1)*4]
    # Reset the indices
    a_slice.reset_index(drop=True, inplace=True)
    # Set the slice into b_df
    b_df.iloc[i,:] = a_slice

或者您可以转换为list而不是使用reset_index

for i in range(2):
    # Get the slice
    a_slice = a_df.iloc[0, i*4:(i+1)*4]
    # Convert the slice into a list and set it into b_df
    b_df.iloc[i,:] = list(a_slice)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.