有效地将值从一列替换到另一列 Pandas DataFrame

Posted

技术标签:

【中文标题】有效地将值从一列替换到另一列 Pandas DataFrame【英文标题】:Efficiently replace values from a column to another column Pandas DataFrame 【发布时间】:2017-02-15 14:49:27 【问题描述】:

我有一个这样的 Pandas DataFrame:

   col1 col2 col3
1   0.2  0.3  0.3
2   0.2  0.3  0.3
3     0  0.4  0.4
4     0    0  0.3
5     0    0    0
6   0.1  0.4  0.4

我想用第二列中的值 (col2) 替换 col1 值,前提是 col1 值等于 0,之后(对于剩余的零值),再做一次,但使用第三列 (col3)。期望的结果是下一个:

   col1 col2 col3
1   0.2  0.3  0.3
2   0.2  0.3  0.3
3   0.4  0.4  0.4
4   0.3    0  0.3
5     0    0    0
6   0.1  0.4  0.4

我是使用pd.replace 函数完成的,但它似乎太慢了。我认为必须是一种更快的方法来完成它。

df.col1.replace(0,df.col2,inplace=True)
df.col1.replace(0,df.col3,inplace=True)

有更快的方法吗?使用其他函数代替pd.replace 函数?

【问题讨论】:

你可以做df.replace(0, pd.np.nan).bfill(axis=1).fillna(0) - 也许不会更快。 【参考方案1】:

使用np.where 更快。使用与 replace 类似的模式:

df['col1'] = np.where(df['col1'] == 0, df['col2'], df['col1'])
df['col1'] = np.where(df['col1'] == 0, df['col3'], df['col1'])

但是,使用嵌套的np.where 会稍微快一些:

df['col1'] = np.where(df['col1'] == 0, 
                      np.where(df['col2'] == 0, df['col3'], df['col2']),
                      df['col1'])

时间安排

使用以下设置生成更大的示例 DataFrame 和计时函数:

df = pd.concat([df]*10**4, ignore_index=True)

def root_nested(df):
    df['col1'] = np.where(df['col1'] == 0, np.where(df['col2'] == 0, df['col3'], df['col2']), df['col1'])
    return df

def root_split(df):
    df['col1'] = np.where(df['col1'] == 0, df['col2'], df['col1'])
    df['col1'] = np.where(df['col1'] == 0, df['col3'], df['col1'])
    return df

def pir2(df):
    df['col1'] = df.where(df.ne(0), np.nan).bfill(axis=1).col1.fillna(0)
    return df

def pir2_2(df):
    slc = (df.values != 0).argmax(axis=1)
    return df.values[np.arange(slc.shape[0]), slc]

def andrew(df):
    df.col1[df.col1 == 0] = df.col2
    df.col1[df.col1 == 0] = df.col3
    return df

def pablo(df):
    df['col1'] = df['col1'].replace(0,df['col2'])
    df['col1'] = df['col1'].replace(0,df['col3'])
    return df

我得到以下时间:

%timeit root_nested(df.copy())
100 loops, best of 3: 2.25 ms per loop

%timeit root_split(df.copy())
100 loops, best of 3: 2.62 ms per loop

%timeit pir2(df.copy())
100 loops, best of 3: 6.25 ms per loop

%timeit pir2_2(df.copy())
1 loop, best of 3: 2.4 ms per loop

%timeit andrew(df.copy())
100 loops, best of 3: 8.55 ms per loop

我尝试为您的方法计时,但它已经运行了数分钟而没有完成。作为比较,仅在 6 行示例 DataFrame(不是上面测试的更大的那个)上计时您的方法需要 12.8 毫秒。

【讨论】:

@piRSquared:添加为pir2_2 可能是for循环。它是较小的 6 行示例 DataFrame 中最快的。 @piRSquared:这加快了速度!【参考方案2】:

我不确定它是否更快,但您是对的,您可以对数据框进行切片以获得所需的结果。

df.col1[df.col1 == 0] = df.col2
df.col1[df.col1 == 0] = df.col3
print(df)

输出:

   col1  col2  col3
0   0.2   0.3   0.3
1   0.2   0.3   0.3
2   0.4   0.4   0.4
3   0.3   0.0   0.3
4   0.0   0.0   0.0
5   0.1   0.4   0.4

或者,如果您希望它更简洁(尽管我不知道它是否更快),您可以将您所做的与我所做的结合起来。

df.col1[df.col1 == 0] = df.col2.replace(0, df.col3)
print(df)

输出:

   col1  col2  col3
0   0.2   0.3   0.3
1   0.2   0.3   0.3
2   0.4   0.4   0.4
3   0.3   0.0   0.3
4   0.0   0.0   0.0
5   0.1   0.4   0.4

【讨论】:

使用 Pandas 的最后一个版本,第一个代码给了我下一个警告:main:1: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from一个数据框 根据this post,实施该警告是为了提醒用户在他们使用链式分配的情况下,而在这种情况下你不是,所以你应该没问题。【参考方案3】:

使用pd.DataFrame.wherepd.DataFrame.bfill 的方法

df['col1'] = df.where(df.ne(0), np.nan).bfill(axis=1).col1.fillna(0)
df

使用np.argmax的另一种方法

def pir2(df):
    slc = (df.values != 0).argmax(axis=1)
    return df.values[np.arange(slc.shape[0]), slc]

我知道有更好的方法可以使用numpy 进行切片。只是暂时想不出来。

【讨论】:

以上是关于有效地将值从一列替换到另一列 Pandas DataFrame的主要内容,如果未能解决你的问题,请参考以下文章

SQL根据条件将值从一列复制到另一列

如何将值从一列映射到另一列数据框? [复制]

如果满足条件,熊猫将值从一列复制到另一列

Postgres 创建触发器函数以在允许插入之前将值从一列复制到另一列

在同一个表中将值从一列复制到另一列

Liquibase:将值从一列复制到具有数组数据类型的另一列