逐行迭代熊猫,并以python方式修改特定"cells"



我是python的新手,我有一个pandas数据帧,我想逐行迭代(例如其他语言中的2d数组(。目标是这样的逻辑:(如果df是一个类似2d的数组(

for row in df:
if df[row,2] == '' AND df[row,1] !='':
df[row-1,1] = df[row,1]
df[row,1] = ''

重点是:我想把当前行的内容移到第1列的前一行,若当前行第2列为空,而当前行第1列不为空。

我该如何用蟒蛇的方式做到这一点?(例如,不使用for循环进行迭代(。我看到了一些关于矢量化的东西,但我真的不明白它是如何工作的。

还是将df转换为列表列表或数组更容易?文件很大,所以我想用一种快速的方法,我从excel文件中读取,所以我只使用panda的read_excel将其导入到df中。

试试这个(假设第1列是指索引0处的列,第2列是索引1处的列(:

import pandas as pd
import numpy as np
col1, col2 = df.columns[0], df.columns[1]
mask = (df.loc[:, col1] != '') & (df.loc[:, col2] == '')
mask.iloc[0] = False  # don't wrap around first row (even if the condition applies)
df.loc[mask.shift(-1, fill_value=False), col1] = df.loc[mask, col1].values

这里的关键点是使用Series.shift将布尔掩码向后移动一。这只使用pandas/numpy矢量化函数,因此它将比使用普通Pythonfor循环进行迭代要好得多。

循序渐进

  1. [获取列的标签:col1, col2 = df.columns[0], df.columns[1]]

  2. 为满足条件的行创建一个布尔掩码True,即第一列为非空,第二列为空:

    mask = (df.loc[:, col1] != '') & (df.loc[:, col2] == '')
    mask.iloc[0] = False
    

    在这里,我们手动将掩码的第一个元素设置为False,因为即使第一行满足条件,我们也不能对它做任何事情(没有前一行可以将第一列的值复制到(。(这对Series.shift来说不是问题,它不会环绕,但当我们在步骤3中使用这个掩码来选择要分配的值时,使用df.loc[mask, col1].values:如果mask.iloc[0]True,我们将比目标多一个值。(

  3. 将掩码向后移动一以获得要修改的行的掩码(即,紧跟在满足条件的行之前的行(:

    mask.shift(-1, fill_value=False)
    

    由于我们将掩码向后移动一,因此不会定义最后一个元素,因此我们使用fill_value=False将其设置为False——我们不想修改最后一行。

  4. 在第1列中,使用我们计算的两个掩码,将满足条件的行的值分配给它们各自的前一行:

    df.loc[mask.shift(-1, fill_value=False), col1] = df.loc[mask, col1].values
    

    在这里,我们必须使用右侧的.values来获得原始numpy值数组,因为如果我们将其保留为Series,Panda将尝试对齐lhs和rhs的索引(由于我们将行移动了一,索引将不匹配,因此最终结果将包含NaNs(;相反,我们只想将rhs的第一个元素分配给lhs的第一时隙,将第二个元素分配到第二时隙,等等。

这与Chaos在评论中概述的方法大致相同。

示例

>>> sample = pd.DataFrame([("spam", ""), ("foo", "bar"), ("baz", ""), ("", "eggs")])
>>> df = sample.copy()
>>> df
0     1
0  spam
1   foo   bar
2   baz
3        eggs
>>> col1, col2 = df.columns[0], df.columns[1]
>>> mask = (df.loc[:, col1] != '') & (df.loc[:, col2] == '')
>>> mask.iloc[0] = False
>>> df.loc[mask.shift(-1, fill_value=False), col1] = df.loc[mask, col1].values
>>> df
0     1
0  spam
1   baz   bar
2   baz
3        eggs

附录

如果您确实想要使第一行的值环绕到最后一行(如果条件适用于第一行(,即您想要循环移动值,则可以使用np.roll而不是Series.shift:

mask = (df.loc[:, col1] != '') & (df.loc[:, col2] == '')
df.loc[np.roll(mask, -1), col1] = np.roll(df.loc[mask, col1].values, -1)

然后,继续前面的例子:

>>> df = sample.copy()
>>> mask = (df.loc[:, col1] != '') & (df.loc[:, col2] == '')
>>> df.loc[np.roll(mask, -1), col1] = np.roll(df.loc[mask, col1].values, -1)
>>> df
0     1
0  spam
1   baz   bar
2   baz
3  spam  eggs

如果你找不到更Python的方法,下面是正确的代码:

for i in range(1, len(df)):
if df.iloc[i, 2]='' and df.iloc[i, 1]!='':
df.iloc[i-1, 1]=df.iloc[i,1]
df.iloc[i, 1]=''

最新更新