【问题标题】:Conditional Weighted Average calculation in pandaspandas 中的条件加权平均计算
【发布时间】:2020-07-26 05:48:21
【问题描述】:

我有 2 个如下数据框

Teacher_Commission_df 如下

+---------+---------+----------+---------+
| Subject |  Harare | Redcliff |  Norton |
+---------+---------+----------+---------+
| Science |  0.100  |   0.125  |  0.145  |
+---------+---------+----------+---------+
| English |  0.125  |   0.150  |  0.170  |
+---------+---------+----------+---------+
|  Maths  |  0.090  |   0.115  |  0.135  |
+---------+---------+----------+---------+
|  Music  |  0.100  |   0.125  |  0.145  |
+---------+---------+----------+---------+
|  Total  |  0.415  |   0.515  |  0.595  |
+---------+---------+----------+---------+

Students_df 如下。 (注意MathsHarareNorton 中没有学生)

+---------+--------+----------+--------+
| Subject | Harare | Redcliff | Norton |
+---------+--------+----------+--------+
| Science |   15   |    18    |   20   |
+---------+--------+----------+--------+
| English |   35   |    33    |   31   |
+---------+--------+----------+--------+
|  Maths  |        |    25    |        |
+---------+--------+----------+--------+
|  Music  |   40   |    42    |   45   |
+---------+--------+----------+--------+

我需要计算每个城市的加权平均佣金,有条件。

首先,我将给出所需的输出并解释方法。

所需的输出如下。

+------------+--------+----------+--------+
| Total_Paid | Harare | Redcliff | Norton |
+------------+--------+----------+--------+
|   Science  |  4.62  |   4.37   |  6.30  |
+------------+--------+----------+--------+
|   English  |  13.46 |   9.61   |  11.46 |
+------------+--------+----------+--------+
|    Maths   |  0.00  |   5.58   |  0.00  |
+------------+--------+----------+--------+
|    Music   |  12.31 |   10.19  |  14.18 |
+------------+--------+----------+--------+

计算方法

如果在任何城市列[Harare, Redcliff, Norton] 中,如果任何学科[Science, English, Maths, Music] 的学生为零,则应在权重中删除该特定subjectTeacher_Commission

例如在Students_df:取Science主题的城市Harare列。因为,MathsHarare 中是zero,所以teacher_Commission 将计算如下。 15 * [0.10 / (0.415 - 0.09)] = 4.62 请注意在总数的分母中删除了 0.09。在Radcliff 中,它在不删除的情况下计算为18 * [0.125 / 0.515] = 4.37

我希望我的解释清楚。

这可以通过使用IF 条件在Microsoft Excel 中轻松完成。但是,我正在寻找一个可扩展的 pandas 解决方案。

我不确定如何开始计算过程。因此,请给我一个开始解决这个问题。

-----------------------------------------------------------------------------------------
 UPDATE
  I've managed to solve this. Refer to my answer below and suggest for any improvements
------------------------------------------------------------------------------------------

【问题讨论】:

  • 您能解释一下您是如何获得 41.5% 和 51.5% 的吗?
  • Teacher_Commission_df 每一列的总和。它实际上是percentage。为了便于解释,我在Calculation methodology 中提到了%。我已经解决了。在下面参考我自己的答案

标签: python pandas numpy


【解决方案1】:

使用熊猫的解决方案

这实际上只是使用 pandas 的两行代码:

import numpy as np
df_tmp = teacher_commission_df[~students_df.isnull()]
df = (df_tmp.div(df_tmp.apply(np.nansum, axis=0)) * students_df).fillna(0)

结果(使用新的3位精度数据。)

In [1]: df
Out[1]:
            Harare   Redcliff     Norton
Subject
Science   4.615385   4.368932   6.304348
English  13.461538   9.611650  11.456522
Maths     0.000000   5.582524   0.000000
Music    12.307692  10.194175  14.184783

上面代码的解释

注意:本说明使用原题中给出的2位精度数据。

In [1]: students_df.isnull()
Out[1]:
         Harare  Redcliff  Norton
Subject
Science   False     False   False
English   False     False   False
Maths      True     False    True
Music     False     False   False
  • 然后,您可以使用 boolean indexing 和 not 运算符 (~) 从 teacher_commission_df 中选择非空值。
In [3]: teacher_commission_df[~students_df.isnull()]
Out[3]:
         Harare  Redcliff  Norton
Subject
Science    0.10      0.13    0.15
English    0.13      0.15    0.17
Maths       NaN      0.12     NaN
Music      0.10      0.13    0.15
  • 让我们将这个临时数据框保存到新变量df_tmp
In [12]: df_tmp = teacher_commission_df[~students_df.isnull()]
  • 现在,我们要将每个单元格中的值除以列值的总和。在 apply()np.nansum 的帮助下计算列值的总和,忽略 nans:
In [14]: df_tmp.apply(np.nansum, axis=0)
Out[14]:
Harare      0.33
Redcliff    0.53
Norton      0.47
dtype: float64
In [15]: df_tmp.div(df_tmp.apply(np.nansum, axis=0))
Out[15]:
           Harare  Redcliff    Norton
Subject
Science  0.303030  0.245283  0.319149
English  0.393939  0.283019  0.361702
Maths         NaN  0.226415       NaN
Music    0.303030  0.245283  0.319149
  • 然后,将数据帧相乘(按元素相乘):
In [16]: df_tmp.div(df_tmp.apply(np.nansum, axis=0)) * students_df
Out[16]:
            Harare   Redcliff     Norton
Subject
Science   4.545455   4.415094   6.382979
English  13.787879   9.339623  11.212766
Maths          NaN   5.660377        NaN
Music    12.121212  10.301887  14.361702
In [17]: (df_tmp.div(df_tmp.apply(np.nansum, axis=0)) * students_df).fillna(0)
Out[17]:
            Harare   Redcliff     Norton
Subject
Science   4.545455   4.415094   6.382979
English  13.787879   9.339623  11.212766
Maths     0.000000   5.660377   0.000000
Music    12.121212  10.301887  14.361702

【讨论】:

  • 看起来很优雅。但是,没有得到所需的答案。请参阅我的问题中提到的所需结果。
  • 啊!对,在最后一个答案中我有一些复制粘贴错误! :) 更正!现在,答案与所需结果相同(不包括一些舍入误差,因为示例数据仅以 2 位精度给出)。
  • 是的。你的代码是正确的。但是,我想知道当我将数据精确到 3 位时,为什么要四舍五入?
  • 原始数据以 2 位精度给出(在我复制数据以创建示例时)。我现在已经用 3 位精度输入数据更新了最终值。
【解决方案2】:

那么,您需要的是数据框中每个空-null 值的行/列索引吗?

你可以使用 numpy.where()。根据您的空对象的数据类型,您可以

  1. 将 df 加载为 np 数组
  2. I,j = np.where(“NaN”)
  3. i 和 j 现在是索引,如果大小相同,您可以使用它们来消除权重,或者使用 dataframe.index 来查找要删除的权重。

根据您的 dtype 将 NaN 替换为 Null 或“”

这类似于您在 Excel 中使用 IF 所做的操作

就我个人而言,我只会制作一个复制数据帧二进制,即在数据帧中有非空值的地方放一个 1,在空位置放一个 0,然后将这两个向量加倍。但这可能是更多的处理开销

【讨论】:

    【解决方案3】:

    基于User : aak 给出的建议。我已经设法完全从numpy 解决了这个问题。

    # Load data and fill N/A values
    Teacher_Commission_df = pd.read_excel('data_Teacher.xlsx',index_col='Subject', skipfooter=1)
    Students_df = pd.read_excel('data_Studenst.xlsx',index_col='Subject')
    Students_df.fillna(value=0, inplace= True)
    
    
    # Convert Dataframes to Numpy Arrays
    T = Teacher_Commission_df.to_numpy(dtype='float')
    S = Students_df.to_numpy(dtype='float')
    
    # Filter index of ZERO values from Students Numpy Array and 
    # replace the correponding Values in teachers Numpy Array
    T[np.where(S == 0)] = 0
    
    # creat a temporary Sum numpy array for calculation
    Total_Teacher = T.sum(axis=0)
    
    #calculate incentives
    Calculations = T * (S/Total_Teacher)
    
    incentives = (pd.DataFrame(Calculations, columns=Students_df.columns, index=Students_df.index)
                      .round(decimals=2)
                      .reset_index())
    incentives
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-22
      • 2021-03-31
      • 1970-01-01
      • 1970-01-01
      • 2016-02-12
      • 2018-05-23
      • 2020-10-08
      • 1970-01-01
      相关资源
      最近更新 更多