Pandas:如何在不循环的情况下将 applymap/apply 函数与数据帧的参数一起使用
Pandas: How to use applymap/apply function with arguements to a dataframe without looping
背景
Link 1 表明 apply 可以应用于系列。我想在 DataFrame 的子集上使用 apply 函数而不循环遍历列。
示例代码
正在创建大小为 7、7 的样本 DataFrame
def f_test_df(n_rows, n_cols):
df1 = pd.DataFrame(np.random.rand(n_rows, n_cols))
df = df1.applymap(lambda x: round(x*10))
return df
np.random.seed(seed=1)
df1 = f_test_df(7, 7)
如果数字在预定义的范围内,所需的函数应该是 return 相同的值,否则根据它是在限制的下限还是上限,相应的值应该是 return编辑。申请的函数如下:
def f_bounds(x, lower, upper):
if x < lower:
return 'lower'
elif x > upper:
return 'upper'
else:
return x
DataFrame 中需要应用函数的选定部分
df1.loc[2:5, 2:5]
应用函数:
lower = 2
upper = 5
df1.loc[2:5, 2:5].apply(f_bounds, args=(lower, upper))
我遇到了以下错误:
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
因此,我改变了方法并使用了跨列循环,如下所示(效果很好):
for j in range(2, 5):
print(df1.loc[2:5, j].apply(f_bounds, args=(lower, upper)))
其他未经测试的方法
Link 2 参考答案 2,建议不要将 applymap
与参数一起使用。所以,我没有使用 applymap
因为该函数需要 2 个额外的参数。读者请注意,applymap
已在答案中使用。
期望的结果
我想实现这个需要争论的功能,而不是将列循环到数据框。
您可以将 stack
与 unstack
一起使用:
lower = 2
upper = 5
a = df1.loc[2:5, 2:5].stack().apply(f_bounds, args=(lower, upper)).unstack()
print (a)
2 3 4 5
2 4 upper lower 2
3 upper upper upper lower
4 lower 4 upper 5
5 upper lower upper upper
或者将DataFrame传递给函数:
def f_bounds(x, lower, upper):
m1 = x < lower
m2 = x > upper
return np.select([m1, m2], ['lower', 'upper'], default=x)
lower = 2
upper = 5
a = pd.DataFrame(f_bounds(df1.loc[2:5, 2:5], 2 ,5),
index=df1.index[2:6],
columns = df1.columns[2:6])
您可以直接使用 applymap
和 lambda
函数,该函数接受 DataFrame window 上的参数。然后就可以直接更新视图更新原来的DataFrame -
df1.loc[2:5, 2:5] = df1.loc[2:5, 2:5].applymap(lambda x: f_bounds(x, lower, upper))
print(df1)
0 1 2 3 4 5 6
0 4 7 0 3 1 1 2
1 3 4 5 4 7 2 9
2 0 7 4 upper lower 2 8
3 10 3 upper upper upper lower 0
4 2 9 lower 4 upper 5 7
5 3 7 upper lower upper upper 7
6 3 8 1 4 9 3 3
编辑:
这是不使用 apply 或 applymap 的另一种方法来完成您想要做的事情
cond1 = df1[(df1.loc[2:5, 2:5]<lower)].notna()
cond2 = df1[(df1.loc[2:5, 2:5]>upper)].notna()
df1_new = df1.where(~cond1, 'lower').where(~cond2, 'upper')
print(df1_new)
0 1 2 3 4 5 6
0 4 7 0 3 1 1 2
1 3 4 5 4 7 2 9
2 0 7 4 upper lower 2 8
3 10 3 upper upper upper lower 0
4 2 9 lower 4 upper 5 7
5 3 7 upper lower upper upper 7
6 3 8 1 4 9 3 3
背景
Link 1 表明 apply 可以应用于系列。我想在 DataFrame 的子集上使用 apply 函数而不循环遍历列。
示例代码
正在创建大小为 7、7 的样本 DataFrame
def f_test_df(n_rows, n_cols):
df1 = pd.DataFrame(np.random.rand(n_rows, n_cols))
df = df1.applymap(lambda x: round(x*10))
return df
np.random.seed(seed=1)
df1 = f_test_df(7, 7)
如果数字在预定义的范围内,所需的函数应该是 return 相同的值,否则根据它是在限制的下限还是上限,相应的值应该是 return编辑。申请的函数如下:
def f_bounds(x, lower, upper):
if x < lower:
return 'lower'
elif x > upper:
return 'upper'
else:
return x
DataFrame 中需要应用函数的选定部分
df1.loc[2:5, 2:5]
应用函数:
lower = 2
upper = 5
df1.loc[2:5, 2:5].apply(f_bounds, args=(lower, upper))
我遇到了以下错误:
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
因此,我改变了方法并使用了跨列循环,如下所示(效果很好):
for j in range(2, 5):
print(df1.loc[2:5, j].apply(f_bounds, args=(lower, upper)))
其他未经测试的方法
Link 2 参考答案 2,建议不要将 applymap
与参数一起使用。所以,我没有使用 applymap
因为该函数需要 2 个额外的参数。读者请注意,applymap
已在答案中使用。
期望的结果
我想实现这个需要争论的功能,而不是将列循环到数据框。
您可以将 stack
与 unstack
一起使用:
lower = 2
upper = 5
a = df1.loc[2:5, 2:5].stack().apply(f_bounds, args=(lower, upper)).unstack()
print (a)
2 3 4 5
2 4 upper lower 2
3 upper upper upper lower
4 lower 4 upper 5
5 upper lower upper upper
或者将DataFrame传递给函数:
def f_bounds(x, lower, upper):
m1 = x < lower
m2 = x > upper
return np.select([m1, m2], ['lower', 'upper'], default=x)
lower = 2
upper = 5
a = pd.DataFrame(f_bounds(df1.loc[2:5, 2:5], 2 ,5),
index=df1.index[2:6],
columns = df1.columns[2:6])
您可以直接使用 applymap
和 lambda
函数,该函数接受 DataFrame window 上的参数。然后就可以直接更新视图更新原来的DataFrame -
df1.loc[2:5, 2:5] = df1.loc[2:5, 2:5].applymap(lambda x: f_bounds(x, lower, upper))
print(df1)
0 1 2 3 4 5 6
0 4 7 0 3 1 1 2
1 3 4 5 4 7 2 9
2 0 7 4 upper lower 2 8
3 10 3 upper upper upper lower 0
4 2 9 lower 4 upper 5 7
5 3 7 upper lower upper upper 7
6 3 8 1 4 9 3 3
编辑:
这是不使用 apply 或 applymap 的另一种方法来完成您想要做的事情
cond1 = df1[(df1.loc[2:5, 2:5]<lower)].notna()
cond2 = df1[(df1.loc[2:5, 2:5]>upper)].notna()
df1_new = df1.where(~cond1, 'lower').where(~cond2, 'upper')
print(df1_new)
0 1 2 3 4 5 6
0 4 7 0 3 1 1 2
1 3 4 5 4 7 2 9
2 0 7 4 upper lower 2 8
3 10 3 upper upper upper lower 0
4 2 9 lower 4 upper 5 7
5 3 7 upper lower upper upper 7
6 3 8 1 4 9 3 3