我有3列应该加权和求和。但是,有时这些列中有Nan值,这会影响要加权和求和的最后一组列。进一步的示例df:
import numpy as np
import pandas as pd
f = { 'A': [1, np.nan, 2, np.nan, 5, 6, np.nan],
'B': [np.nan, np.nan, 1, 1, 1, np.nan, 7],
'C': [np.nan, 2, 3, 6, np.nan, 5, np.nan]}
fd = pd.DataFrame(data = f)
fd.head(10)
A B C
0 1.0 NaN NaN
1 NaN NaN 2.0
2 2.0 1.0 3.0
3 NaN 1.0 6.0
4 5.0 1.0 NaN
5 6.0 NaN 5.0
6 NaN 7.0 NaN
此示例演示了列中Nan的所有可能组合。然后,我想创建列F,这是列A,B和C不是Nan时的加权总和。这是我的代码:
def scaler(df):
"Scaling and summing"
if (pd.notnull(df['A']) == True & pd.notnull(df['B']) == True & pd.notnull(df['C']) == True):
return df['A']*0.5+df['B']*0.25+df['C']*0.25
elif (pd.notnull(df['A']) == True & pd.notnull(df['B']) == False & pd.notnull(df['C']) == False):
return df['A']*1
elif (pd.notnull(df['A']) == True & pd.notnull(df['B']) == True & pd.notnull(df['C']) == False):
return df['A']*0.75+df['B']*0.25
elif (pd.notnull(df['A']) == True & pd.notnull(df['B']) == False & pd.notnull(df['C']) == True):
return df['A']*0.75+df['C']*0.25
elif (pd.notnull(df['A']) == False & pd.notnull(df['B']) == True & pd.notnull(df['C']) == True):
return df['B']*0.5+df['C']*0.5
elif (pd.notnull(df['A']) == False & pd.notnull(df['B']) == True & pd.notnull(df['C']) == False):
return df['B']*1
else:
return df['C']*1
fd['F'] =fd.apply(scaler, axis = 'columns')
fd.head(10)
A B C F
0 1.0 NaN NaN NaN
1 NaN NaN 2.0 NaN
2 2.0 1.0 3.0 2.0
3 NaN 1.0 6.0 6.0
4 5.0 1.0 NaN NaN
5 6.0 NaN 5.0 5.0
6 NaN 7.0 NaN 7.0
因此,我得到一个df,在其中正确加权,并且仅对所有三个非Nan值的列求和。如果其中一列中至少有一个Nan,则我在F列中得到Nan或错误的结果值。
为解决此问题,我用一些浮点数替换了原始df中的所有Nan值,该浮点数超出了所有列的范围,然后在上面展示了完美的代码逻辑。我的问题是:
1)为什么会发生(尽管包含这些值的列不直接参与重新调整的公式,但所有Nan值都会在结果周围翻转)?
2)我克服问题的方式发现自己有点草率。是否有更优雅的解决方案?
答案 0 :(得分:1)
您误解了pd.DataFrame.apply
的工作方式。沿着axis=1
,每个行都传递给函数,而不是整个数据帧。相应地命名函数参数很有用。
您正在函数中使用非序列的标量,因此应使用常规的and
而不是&
。还要注意,pd.isnull
和pd.notnull
都存在。因此,您可以重写如下:
def scaler(row):
"Scaling and summing"
if pd.notnull(row['A']) and pd.notnull(row['B']) and pd.notnull(row['C']):
return row['A']*0.5 + row['B']*0.25 + row['C']*0.25
elif pd.notnull(row['A']) and pd.isnull(row['B']) and pd.isnull(row['C']):
return row['A']
elif pd.notnull(row['A']) and pd.notnull(row['B']) and pd.isnull(row['C']):
return row['A']*0.75 + row['B']*0.25
elif pd.notnull(row['A']) and pd.isnull(row['B']) and pd.notnull(row['C']):
return row['A']*0.75 + row['C']*0.25
elif pd.isnull(row['A']) and pd.notnull(row['B']) and pd.notnull(row['C']):
return row['B']*0.5 + row['C']*0.5
elif pd.isnull(row['A']) and pd.notnull(row['B']) and pd.isnull(row['C']):
return row['B']
else:
return row['C']
df['F'] = df.apply(scaler, axis=1)
但这对于大量的行而言效率低下。使用np.select
的解决方案更有效,更易读。这些仅使用向量化操作。注意,我们只计算一次检查每个系列中的值是否为空。
a_null = df['A'].isnull()
b_null = df['B'].isnull()
c_null = df['C'].isnull()
conds = [~a_null & b_null & c_null,
a_null & ~b_null & c_null,
a_null & b_null & ~c_null,
~a_null & ~b_null & c_null,
~a_null & b_null & ~c_null,
a_null & ~b_null & ~c_null,
~a_null & ~b_null & ~c_null]
choices = [df['A'], df['B'], df['C'],
0.75 * df['A'] + 0.25 * df['B'],
0.75 * df['A'] + 0.25 * df['C'],
0.5 * df['B'] + 0.5 * df['C'],
0.5 * df['A'] + 0.25 * df['B'] + 0.25 * df['C']]
df['F'] = np.select(conds, choices)
结果:
A B C F
0 1.0 NaN NaN 1.00
1 NaN NaN 2.0 2.00
2 2.0 1.0 3.0 2.00
3 NaN 1.0 6.0 3.50
4 5.0 1.0 NaN 4.00
5 6.0 NaN 5.0 5.75
6 NaN 7.0 NaN 7.00