在groupby.agg

时间:2016-07-12 21:05:28

标签: python pandas

以下是我的数据帧的一小部分样本,长度为25000多行:

 In [58]: df
 Out[58]:
 Send_Agent  Send_Amount
0      ADR000264   361.940000
1      ADR000264    12.930000
2      ADR000264    11.630000
3      ADR000264    12.930000
4      ADR000264    64.630000
5      ADR000264    12.930000
6      ADR000264    77.560000
7      ADR000264   145.010000
8      API185805   112.34
9      API185805   56.45
10     API185805   48.97
11     API185805   85.44
12     API185805   94.33
13     API185805   116.45

有2个Send_Agents ADR000264和API185805。我正在尝试将Benford的法律测试应用于Send_Amount。无论Send_Agent如何,当我尝试所有Send_Amount时,我都能成功完成。以下是我提取前导数字的功能。

def leading_digit(x,dig=1):
    x = str(x)
    out = int(x[dig-1])
    return out 

此函数应用于Send_Amount列时,可以正常工作:

  In [75]: df['Send_Amount'].apply(leading_digit)
  Out[75]:
   0        3
   1        1
   2        1
   3        1
   4        6
   5        1
   6        7
   7        1
   8        1

它提供一个系列输出,并从Send_Amount列中提取前导数字。

但是当我在Send_Agent分组后尝试相同的功能时,我得到了错误的结果:

In [74]: df['Send_Amount'].groupby(df['Send_Agent']).apply(leading_digit)
Out[74]:
Send_Agent
ADR000264    0
API185805    6
dtype: int64

与groupby.agg相同

In [59]: grouped = df.groupby('Send_Agent')
In [60]: a = grouped.agg({'Send_Amount':leading_digit})

In [61]: a
Out[61]:
                  Send_Amount
     Send_Agent
     ADR000264             0
     API185805             6

编辑:

所以,现在我们有领先数字的计数。

   In [16]: result = df.assign(Leading_Digit =    df['Send_Amount'].astype(str).str[0]).groupby('Send_Agent')['Leading_Digit'].value_counts(sort=False)

In [17]: result
Out[17]:
 Send_Agent         Leading_Digit
 ADR000264        1                5509
                  2                4748
                  3                2090
                  4                2497
                  5                 979
                  6                1206
                  7                 529
                  8                 549
                  9                 729
 API185805        1                1707
                  2                1966
                  3                 744
                  4                1218
                  5                 306
                  6                605
                  7                 138
                  8                 621
                  9                  76

dtype:int64

        In [18]: type(result)
        Out[18]: pandas.core.series.Series

我不需要绘制图表。我只需要从benford值中减去计数。

   In [22]: result = result.to_frame()

   In [29]: result.columns = ['Count']

   In [32]: result
   Out[32]:
                                     Count
    Send_Agent  Leading_Digit
  ADR000264  1                        5509
             2                        4748
             3                        2090
             4                        2497
             5                        979
             6                        1206
             7                         529
             8                         549
             9                         729
  API185805  1                         1707
             2                         1966
             3                         744
             4                         1218
             5                         306
             6                         605
             7                         138
             8                         621
             9                         76

         In [33]: result['Count'] = (result['Count'])/(result['Count'].sum())

         In [34]: result
         Out[34]:
                                Count
         Send_Agent Leading_Digit
         ADR000264  1                    0.210131
                    2                    0.181104
                    3                    0.079719
                    4                     0.095244
                    5                    0.037342
                    6                     0.046001
                    7                     0.020178
                    8                     0.020941
                    9                     0.027806
         API185805  1                     0.065110
                    2                     0.074990
                    3                     0.028379
                    4                     0.046458
                    5                     0.011672
                    6                     0.023077
                    7                     0.005264
                    8                    0.023687
                    9                     0.002899

    In [35]: result.unstack()
    Out[35]:
                Count                                                    \
     Leading_Digit         1         2         3         4         5        6
    Send_Agent
    ADR000264      0.210131  0.181104  0.079719  0.095244  0.037342      0.046001
    API185805      0.065110  0.074990  0.028379  0.046458  0.011672  0.023077


   Leading_Digit         7         8         9
    Send_Agent
    ADR000264      0.020178  0.020941  0.027806
    API185805      0.005264  0.023687  0.002899

So , benford values for 1 to 9 as follows 
d =  0.30103, 0.176091,  0.124939,  0.09691,  0.0791812,  0.0669468,    0.0579919,  0.0511525,  0.0457575

我需要做的就是从结果[count]中减去它们。

我仍然是Pandas和Python的新手。那么,我该怎么做。

2 个答案:

答案 0 :(得分:1)

您可以将transformastype一起使用,因为aggapply汇总输出:

print (df['Send_Amount'].astype(str).str[0].astype(int))
0     3
1     1
2     1
3     1
4     6
5     1
6     7
7     1
8     1
9     5
10    4
11    8
12    9
13    1
Name: Send_Amount, dtype: int32

print (df.groupby('Send_Agent')['Send_Amount'].transform(lambda x: x.astype(str).str[0])
         .astype(int))
0     3
1     1
2     1
3     1
4     6
5     1
6     7
7     1
8     1
9     5
10    4
11    8
12    9
13    1
Name: Send_Amount, dtype: int32

如果数字高于9,请使用str[:2]

print (df['Send_Amount'].astype(str).str[:2].astype(int))
0     36
1     12
2     11
3     12
4     64
5     12
6     77
7     14
8     11
9     56
10    48
11    85
12    94
13    11
Name: Send_Amount, dtype: int32

Transformation

答案 1 :(得分:1)

酷项目。我将使用随机生成的数据集进行说明:

import numpy as np
import pandas as pd
np.random.seed(0)
Send_Amount = 10**(np.random.randint(1, 9, 10**6)) * \
                  (np.random.choice(np.arange(1, 10), 
                                    p=np.log10(1+(1/np.arange(1, 10))), 
                                    size=10**6) + 
                   np.random.rand(10**6))
Send_Agent = np.random.choice(['ADR000264', 'API185805'], 10**6)
df = pd.DataFrame({'Send_Agent': Send_Agent, 'Send_Amount': Send_Amount.astype(int)})

它看起来像这样:

df.head()
Out[104]: 
  Send_Agent  Send_Amount
0  ADR000264       370394
1  ADR000264    239323923
2  API185805      6364712
3  ADR000264           98
4  ADR000264        56926

现在,如果将该函数应用于系列Send_Amount,它将返回另一个带有前导数字的系列。如果先对它们进行分组,则需要指定每个组所需的结果类型。该功能不是为了获取组而返回该组的结果。它只返回一个数字的前导数字。

相反,要验证Benford's law,您需要检查前导数字的频率分布。由于您已经为前导数字创建了一列,现在您可以通过Send_Agent进行分组并在该列上调用value_counts。在一行中,它看起来像这样:

result = df.assign(Leading_Digit = df['Send_Amount'].astype(str).str[0]).groupby('Send_Agent')['Leading_Digit'].value_counts(sort=False)
print(result)
Out[105]: 
Send_Agent  Leading_Digit
ADR000264   1                150522
            2                 87739
            3                 62460
            4                 48204
            5                 39757
            6                 33791
            7                 29024
            8                 25567
            9                 23044
API185805   1                150575
            2                 87994
            3                 62173
            4                 48323
            5                 39452
            6                 33720
            7                 29141
            8                 25538
            9                 22976
Name: Leading_Digit, dtype: int64

您也可以使用df.groupby('Send_Agent')['Leading_Digit'].value_counts(sort=False)完成此操作(在创建列之后)。我只是一步完成了它们。最终,分发将(希望)看起来像这样:

result.unstack(level=0).plot.bar(subplots=True)

enter image description here

要找出理论概率与观察到的频率之间的差异,您可以这样做:

result = df.assign(Leading_Digit = df['Send_Amount'].astype(str).str[0]).groupby('Send_Agent')['Leading_Digit'].value_counts(sort=False, normalize=True)

请注意,我通过normalize=True以便计算比例而不是频率。

现在你可以采取以下方式:

result.unstack(level=0).subtract(np.log10(1+(1/np.arange(1, 10))), axis=0).abs()
Out[16]: 
Send_Agent     ADR000264  API185805
Leading_Digit                      
1               0.000051   0.000185
2               0.000651   0.000065
3               0.000046   0.000566
4               0.000523   0.000243
5               0.000316   0.000260
6               0.000621   0.000508
7               0.000044   0.000303
8               0.000030   0.000065
9               0.000321   0.000204 

这里,unstack将Send_Agent带到列中。 np.log10(1+(1/np.arange(1, 10)))计算理论概率。您也可以传递先前定义的数组。由于我们希望逐行减去元素,因此axis=0方法有subtract个参数。最后,.abs()取结果的绝对值。