使用loc对Pandas DataFrame进行选择性操作,而不会覆盖未选择的行

时间:2019-12-17 11:10:17

标签: python pandas dataframe

我想应用算术运算来选择Pandas DataFrame的行,选择取决于另一列。

我首先尝试定义一个函数并使用apply来计算值,但这太慢了,因为我猜想每行都要评估函数逻辑。

使用loc应用操作要快得多,但是当我连续应用此操作时,未选择的行每次都会被NaN覆盖。

一个说明性的例子是:

new_df = pd.DataFrame(
    [[1, 0.1], [1, 0.2], [1, 0.3], [2, 0.4], [2, 0.5]], columns=["class", "size"]
)
new_df
#    class  size
#    1      0.1
#    1      0.2
#    1      0.3
#    2      0.4
#    2      0.5

说我想对类== 2的所有行的大小值求平方,并对类== 1的所有行将其值求立方。

目标DataFrame是

#    class  size oper_size
#    1      0.1  0.001
#    1      0.2  0.008
#    1      0.3  0.027
#    2      0.4  0.16
#    2      0.5  0.25

但是,如果我这样做:

new_df["oper_size"] = new_df["size"].loc[new_df["class"] == 1] ** 3
new_df["oper_size"] = new_df["size"].loc[new_df["class"] == 2] ** 2

然后得到的数据框是:

#  class  size  oper_size
#      1   0.1        NaN
#      1   0.2        NaN
#      1   0.3        NaN
#      2   0.4       0.16
#      2   0.5       0.25

任何帮助都将不胜感激。

2 个答案:

答案 0 :(得分:1)

您需要使用loc选择要替换的行。例如

new_df.loc[new_df["class"] == 1, "oper_size"] = new_df[new_df["class"] == 1, "oper_size"] ** 3

或定义可用于两面的遮罩:

mask_1 = new_df["class"] == 1
new_df.loc[mask_1, "oper_size"] = new_df[mask_1, "oper_size"] ** 3

或者,您也可以使用numpy.where来区分2种情况:

new_df['oper_size'] = np.where(new_df['class'] == 1, 
                               new_df['size']**3,
                               new_df['size']**2)
new_df
   class  size  oper_size
0      1   0.1      0.001
1      1   0.2      0.008
2      1   0.3      0.027
3      2   0.4      0.160
4      2   0.5      0.250

答案 1 :(得分:1)

您可以为class的每个值创建字典,因此可以对新系列使用Series.map,然后仅对**使用幂运算:

new_df["oper_size"] = new_df["size"] ** new_df['class'].map({1:3, 2:2})
print (new_df)
   class  size  oper_size
0      1   0.1      0.001
1      1   0.2      0.008
2      1   0.3      0.027
3      2   0.4      0.160
4      2   0.5      0.250

详细信息

print (new_df['class'].map({1:3, 2:2}))
0    3
1    3
2    3
3    2
4    2
Name: class, dtype: int64

如果某些值不匹配,例如最后一行,则可以将所有值替换为某个数字,例如0或某列,例如size

new_df = pd.DataFrame(
    [[1, 0.1], [1, 0.2], [1, 0.3], [2, 0.4], [3, 0.5]], columns=["class", "size"]
)


new_df["oper_size1"]=(new_df["size"] ** new_df['class'].map({1:3, 2:2})).fillna(0)
new_df["oper_size2"]=(new_df["size"] ** new_df['class'].map({1:3, 2:2})).fillna(new_df["size"])

print (new_df)
   class  size  oper_size1  oper_size2
0      1   0.1       0.001       0.001
1      1   0.2       0.008       0.008
2      1   0.3       0.027       0.027
3      2   0.4       0.160       0.160
4      3   0.5       0.000       0.500