如何通过根据条件排除几列来计算所有行的平均值

How to calculate average of all rows by excluding a few columns based on a condition

我有一个棘手的数据清理要求,使用 pandas。我有一个只有二进制值的以下数据框。每行代表客户对调查问题的回答。下面的 df 有两个问题(q1 和 q2),他们的回答用“_”符号表示。例如,客户 aa​​sww 提供 'a' 作为对 q1 的响应,并提供 'b' 作为对 q2 的响应。在某些情况下,客户可以跳过由 0 表示的问题(例如,客户 asdsd 跳过了 q1)

Customer_id q1_a q1_b q1_0 q2_a q2_b q2_c q2_0
asdsd 0 0 1 0 0 1 0
aasww 1 0 0 0 1 0 0
aaswe 0 1 0 0 0 0 1
aaswt 0 1 0 1 0 0 0

现在,我想通过排除具有“_0”的列来计算每列所有客户的平均值。但这里棘手的部分是,我只想计算回答了该特定问题的客户的平均值。例如,在上面的数据框中,客户 asdsd 跳过了 q1,因此在计算 Q1 的平均值时应该忽略该客户。这是预期的输出,

Customer_id q1_a q1_b q2_a q2_b q2_c
asdsd 0 0 0 0 1
aasww 1 0 0 1 0
aaswe 0 1 0 0 0
aaswt 0 1 1 0 0
AVERAGE 33.3% 66.7% 33.3% 33.3% 33.3%

此外,我有 100 多个这样的专栏(调查中的问题太多)所以如果我们能有一个循环解决它的解决方案就太好了

这是使用 melt 的一种方法。这个想法是,我们过滤 DataFrame 的相关部分并 melt 它;然后过滤掉对一个问题的所有答案为 0 的客户(构建一个过滤器,将 all 转换为 groupby.transform);然后对于剩余的客户,使用 groupby.mean 找到 mean 响应。最后,将这些方法分配回 out DataFrame:

out = df.loc[:, ~df.columns.str.endswith('_0')].copy()
df1 = out.melt('Customer_id')
df1 = df1.join(df1.pop('variable').str.split('_', expand=True))
percentages = (df1.loc[~df1['value'].eq(0).groupby([df1['Customer_id'], df1[0]]).transform('all')]
               .groupby([ 0, 1])['value'].mean() * 100)
percentages.index = percentages.index.map('_'.join)
out.loc[len(out)] = percentages
out.loc[len(out)-1, 'Customer_id'] = 'Average'

输出:

  Customer_id       q1_a       q1_b       q2_a       q2_b       q2_c
0       asdsd   0.000000   0.000000   0.000000   0.000000   1.000000
1       aasww   1.000000   0.000000   0.000000   1.000000   0.000000
2       aaswe   0.000000   1.000000   0.000000   0.000000   0.000000
3       aaswt   0.000000   1.000000   1.000000   0.000000   0.000000
4     Average  33.333333  66.666667  33.333333  33.333333  33.333333
  1. 获取回答每个问题的客户数量
  2. 将每列的总和除以回答的数字
#set customer_id as the index
df = df.set_index("Customer_id")

#filter columns that do not end with _0
answered = df.filter(regex="^(?!.*_0$)")

#map the column names to the number of customers that answered
counter = df.columns.str.split("_").str[0].map(answered.groupby(answered.columns.str.split("_").str[0],axis=1).sum().sum())

#compute averages
df.loc["Average"] = df.sum().div(counter)

#keep only answered columns in the output
output = df[answered.columns]

>>> df
                 q1_a      q1_b      q2_a      q2_b      q2_c
Customer_id                                                  
asdsd        0.000000  0.000000  0.000000  0.000000  1.000000
aasww        1.000000  0.000000  0.000000  1.000000  0.000000
aaswe        0.000000  1.000000  0.000000  0.000000  0.000000
aaswt        0.000000  1.000000  1.000000  0.000000  0.000000
Average      0.333333  0.666667  0.333333  0.333333  0.333333