【问题标题】:Pass subset of df to function - Python将 df 的子集传递给函数 - Python
【发布时间】:2020-02-21 00:05:41
【问题描述】:

我手动将 pandas df 中的特定值传递给函数。这很好,但我希望让这个过程更有效率。具体来说,我首先对Item 中的所有连续值进行子集化。然后我将Val 中的相应值传递给func。这会产生我需要的值。这对于较小的 df 是可以的,但对于较大的数据集变得低效。

我只是希望使这个过程更有效地将值应用于原始 df。

import pandas as pd
import numpy as np

df = pd.DataFrame({ 
            'Time' : ['1','2','3','4','5','6','7','8','9','10','11','12','13','14','15'],                   
            'Val' : [35,38,31,30,35,31,32,34,36,38,39,30,25,26,27],                   
            'Item' : ['X','X','X','X','X','Y','Y','Y','Y','Y','Y','X','X','X','X'],  
                    })

df1 = df.groupby([df['Item'].ne(df['Item'].shift()).cumsum(), 'Item']).size()

X1 = df[0:5]
Y1 = df[5:11]
X2 = df[11:15]

V1 = X1['Val1'].reset_index(drop = True)
V2 = Y1['Val1'].reset_index(drop = True)
V3 = X2['Val1'].reset_index(drop = True)

def func(U, m = 2, r = 0.2):

        def _maxdist(x_i, x_j):
            return max([abs(ua - va) for ua, va in zip(x_i, x_j)])

        def _phi(m):
            x = [[U[j] for j in range(i, i + m - 1 + 1)] for i in range(N - m + 1)]
            C = [len([1 for x_j in x if _maxdist(x_i, x_j) <= r]) / (N - m + 1.0) for x_i in x]
            return (N - m + 1.0)**(-1) * sum(np.log(C))

        N = len(U)

        return abs(_phi(m + 1) - _phi(m))

print(func(V1))
print(func(V2))
print(func(V3))

出来:

0.287682072452
0.223143551314
0.405465108108

如果我只是尝试使用groupby 应用该函数,它会返回KeyError: 0。除非我重置索引,否则该功能不起作用。

df1 = df.groupby(['Item']).apply(func)

密钥错误:0

预期输出:

   Time  Val1 Item   func
0     1    35    X  0.287
1     2    38    X  0.287
2     3    31    X  0.287
3     4    30    X  0.287
4     5    35    X  0.287
5     6    31    Y  0.223
6     7    32    Y  0.223
7     8    34    Y  0.223
8     9    36    Y  0.223
9    10    38    Y  0.223
10   11    39    Y  0.223
11   12    30    X  0.405
12   13    25    X  0.405
13   14    26    X  0.405
14   15    27    X  0.405

【问题讨论】:

    标签: python pandas function


    【解决方案1】:

    问题出在_phi 函数中的U[j]。它的j 是位置索引,因此您可以使用U.iloc[j] 或将其更改为列表并直接从列表中工作。它似乎比使用iloc 更快地处理列表。我的修复将其更改为列表并在列表中工作。 _phi 中的 x = ... 行也可以使用一些修改使其更短。

    方法一

    def func(U, m = 2, r = 0.2):
    
        def _maxdist(x_i, x_j):
            return max([abs(ua - va) for ua, va in zip(x_i, x_j)])
    
        def _phi(m):
            x = [U.tolist()[i:i + m] for i in range(N - m + 1)] #change at this line
            C = [len([1 for x_j in x if _maxdist(x_i, x_j) <= r]) / (N - m + 1.0) for x_i in x]
            return (N - m + 1.0)**(-1) * sum(np.log(C))
    
        N = len(U)
    
        return abs(_phi(m + 1) - _phi(m))
    

    像您一样创建自定义 groupID s 并在 s 上创建 groupby 并调用 transform

    s = df['Item'].ne(df['Item'].shift()).cumsum()
    df['func'] = df.groupby(s).Val.transform(func)
    
    Out[1090]:
       Time  Val Item      func
    0     1   35    X  0.287682
    1     2   38    X  0.287682
    2     3   31    X  0.287682
    3     4   30    X  0.287682
    4     5   35    X  0.287682
    5     6   31    Y  0.223144
    6     7   32    Y  0.223144
    7     8   34    Y  0.223144
    8     9   36    Y  0.223144
    9    10   38    Y  0.223144
    10   11   39    Y  0.223144
    11   12   30    X  0.405465
    12   13   25    X  0.405465
    13   14   26    X  0.405465
    14   15   27    X  0.405465
    

    方法2:更短但可读性较差。使用as_strided 来自numpy.lib.stride_tricks

    def func(U, m = 2, r = 0.2):
    
        def _phi(m):
            strd = U.to_numpy().strides[0]
            x = as_strided(U.to_numpy(), (N-m+1, m), (strd, strd))
            C = (np.abs(x - x[:,None]).max(-1) <= r).sum(-1) / (N - m + 1.0)    
            return np.sum(np.log(C)) / (N - m + 1.0)
    
        N = len(U)
    
        return abs(_phi(m + 1) - _phi(m))      
    

    您需要导入as_strided并创建groupID并调用groupby转换作为方法1

    from numpy.lib.stride_tricks import as_strided
    
    s = df['Item'].ne(df['Item'].shift()).cumsum()
    df['func'] = df.groupby(s).Val.transform(func)
    

    【讨论】:

      【解决方案2】:

      看来您正在使用applyfunc,但func 不准备直接接收整个数据帧片段。在这种情况下,lambda expressions 很有用。

      你可以这样做:

      # Fisrt, convert each item (string) to a unique value (integer) (based on solution here: https://stackoverflow.com/questions/31701991/string-of-text-to-unique-integer-method)
      df['ItemID'] = df['Item'].apply(lambda s: int.from_bytes(s.encode(), 'little'))
      
      # Get the consecutive items (based on solution here: https://stackoverflow.com/questions/26911851/how-to-use-pandas-to-find-consecutive-same-data-in-time-series)
      ItemConsecutive = (np.diff(df['ItemID'].values) != 0).astype(int).cumsum()
      ItemConsecutive = np.insert(ItemConsecutive, 0, ItemConsecutive[0])
      df['ItemConsecutive'] = ItemConsecutive
      
      # Define your custom func (unmodified)
      def func(U, m = 2, r = 0.2):
          def _maxdist(x_i, x_j):
              return max([abs(ua - va) for ua, va in zip(x_i, x_j)])
          def _phi(m):
              x = [[U[j] for j in range(i, i + m - 1 + 1)] for i in range(N - m + 1)]
              C = [len([1 for x_j in x if _maxdist(x_i, x_j) <= r]) / (N - m + 1.0) for x_i in x]
              return (N - m + 1.0)**(-1) * sum(np.log(C))
          N = len(U)
          return abs(_phi(m + 1) - _phi(m))
      
      # Get your calculated values with func based on each consecutive item
      func_values = df.groupby('ItemConsecutive').apply(lambda x: func(x['Val'].reset_index(drop=True)))
      func_values.name = 'func'
      
      # Complete the dataframe with you calculated values
      df = df.join(func_values, on='ItemConsecutive')
      

      这是结果:

         Item Time  Val  ItemID  ItemConsecutive      func
      0     X    1   35      88                0  0.287682
      1     X    2   38      88                0  0.287682
      2     X    3   31      88                0  0.287682
      3     X    4   30      88                0  0.287682
      4     X    5   35      88                0  0.287682
      5     Y    6   31      89                1  0.223144
      6     Y    7   32      89                1  0.223144
      7     Y    8   34      89                1  0.223144
      8     Y    9   36      89                1  0.223144
      9     Y   10   38      89                1  0.223144
      10    Y   11   39      89                1  0.223144
      11    X   12   30      88                2  0.405465
      12    X   13   25      88                2  0.405465
      13    X   14   26      88                2  0.405465
      14    X   15   27      88                2  0.405465
      

      顺便说一句,我使用的是熊猫版本 0.23.3

      【讨论】:

        【解决方案3】:

        需要在groupby之后使用apply: https://pandas.pydata.org/pandas-docs/stable/user_guide/groupby.html

        df1 = df.groupby(['Item']).apply( lambda x : myfunc(x) )
        

        myfunc 对按“项目”分组的子数据帧进行操作。

        【讨论】:

          猜你喜欢
          • 2012-08-06
          • 2020-07-29
          • 2019-12-21
          • 2019-08-21
          • 1970-01-01
          • 2015-10-12
          • 2015-01-23
          • 1970-01-01
          相关资源
          最近更新 更多