【问题标题】:How to filter a Dataframe based on a criteria using .shift()如何使用 .shift() 根据条件过滤数据框
【发布时间】:2015-02-06 21:18:24
【问题描述】:

我正在尝试从 groupby 中的第一个非顺序“期间”开始删除数据框中的任何行。如果可能的话,我宁愿避免循环。

import pandas as pd


data = {'Country': ['DE', 'DE', 'DE', 'DE', 'DE', 'US', 'US', 'US', 'US','US'],
    'Product': ['Blue', 'Blue', 'Blue', 'Blue','Blue','Green', 'Green', 'Green', 'Green','Green'],
    'Period': [1, 2, 3,5,6, 1, 2, 4, 5, 6]}

df = pd.DataFrame(data, columns= ['Country','Product', 'Period'])
print df

输出:

  Country Product  Period
0      DE    Blue       1
1      DE    Blue       2
2      DE    Blue       3
3      DE    Blue       5
4      DE    Blue       6
5      US   Green       1
6      US   Green       2
7      US   Green       4
8      US   Green       5
9      US   Green       6

例如,我想要的最终输出如下:

  Country Product  Period
0      DE    Blue       1
1      DE    Blue       2
2      DE    Blue       3
5      US   Green       1
6      US   Green       2

我试图这样做的方式如下,可以给你一个想法,但我有很多错误。但是您可能会看到我正在尝试做的事情的逻辑。

df = df.groupby(['Country','Product']).apply(lambda x: x[x.Period.shift(x.Period - 1) == 1]).reset_index(drop=True)

棘手的部分不仅仅是使用 .shift(1) 或我试图在 .shift() 中输入一个值的东西,即如果该行 Period 为 5,那么我想说 .shift(5-1)因此它向上移动 4 个位置并检查该 Period 的值。如果它等于 1,则表示它仍然是连续的。在这种情况下,我猜它会进入南区。

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    您可以使用diff()cumsum(),而不是使用shift()

    result = grouped['Period'].apply(
        lambda x: x.loc[(x.diff() > 1).cumsum() == 0])
    

    import pandas as pd
    
    data = {'Country': ['DE', 'DE', 'DE', 'DE', 'DE', 'US', 'US', 'US', 'US','US'],
        'Product': ['Blue', 'Blue', 'Blue', 'Blue','Blue','Green', 'Green', 'Green', 'Green','Green'],
        'Period': [1, 2, 3,5,6, 1, 2, 4, 5, 6]}
    
    df = pd.DataFrame(data, columns= ['Country','Product', 'Period'])
    print(df)
    grouped = df.groupby(['Country','Product'])
    result = grouped['Period'].apply(
        lambda x: x.loc[(x.diff() > 1).cumsum() == 0])
    result.name = 'Period'
    result = result.reset_index(['Country', 'Product'])
    print(result)
    

    产量

      Country Product  Period
    0      DE    Blue       1
    1      DE    Blue       2
    2      DE    Blue       3
    5      US   Green       1
    6      US   Green       2
    

    解释

    连续数字的相邻差异为 1。 例如,如果我们目前将df['Period'] 视为所有一个组的一部分,

    In [41]: df['Period'].diff()
    Out[41]: 
    0   NaN
    1     1
    2     1
    3     2
    4     1
    5    -5
    6     1
    7     2
    8     1
    9     1
    Name: Period, dtype: float64
    
    In [42]: df['Period'].diff() > 1
    Out[42]: 
    0    False
    1    False
    2    False
    3     True       <--- We want to cut off before here
    4    False
    5    False
    6    False
    7     True
    8    False
    9    False
    Name: Period, dtype: bool
    

    要查找截止位置 -- df['Period'].diff() &gt; 1 中的第一个 True -- 我们可以使用cumsum(),并选择那些等于0的行:

    In [43]: (df['Period'].diff() > 1).cumsum()
    Out[43]: 
    0    0
    1    0
    2    0
    3    1
    4    1
    5    1
    6    1
    7    2
    8    2
    9    2
    Name: Period, dtype: int64
    
    In [44]: (df['Period'].diff() > 1).cumsum() == 0
    Out[44]: 
    0     True
    1     True
    2     True
    3    False
    4    False
    5    False
    6    False
    7    False
    8    False
    9    False
    Name: Period, dtype: bool
    

    使用diff()cumsum() 可能看起来很浪费,因为这些操作可能 计算很多不需要的值——特别是如果x 非常 很大,第一次连续运行很短。

    尽管浪费,但调用 NumPy 或 Pandas 方法获得的速度 (在 C/Cython/C++ 或 Fortran 中实现)通常会压倒更少的浪费 用纯 Python 编码的算法。

    但是,您可以将调用 cumsum 替换为调用 argmax

    result = grouped['Period'].apply(
        lambda x: x.loc[:(x.diff() > 1).argmax()].iloc[:-1])
    

    对于非常大的x,这可能会更快:

    x = df['Period']
    x = pd.concat([x]*1000)
    x = x.reset_index(drop=True)
    
    In [68]: %timeit x.loc[:(x.diff() > 1).argmax()].iloc[:-1]
    1000 loops, best of 3: 884 µs per loop
    
    In [69]: %timeit x.loc[(x.diff() > 1).cumsum() == 0]
    1000 loops, best of 3: 1.12 ms per loop
    

    但请注意,argmax 返回的是索引级别值,而不是序数索引 地点。因此,如果x.index 包含重复项,则使用 argmax 将不起作用 价值观。 (这就是为什么我必须设置x = x.reset_index(drop=True)。)

    因此,虽然在某些情况下使用 argmax 会更快一些,但这种替代方案并不那么可靠。

    【讨论】:

      【解决方案2】:

      对不起..我不知道pandas..但通常可以直接在python中实现。

      zip(data['Country'],data['Product'],data['Period'])
      and the result will be a list ..
      [('DE', 'Blue', 1), ('DE', 'Blue', 2), ('DE', 'Blue', 3), ('DE', 'Blue', 5), 
      ('DE', 'Blue', 6), ('US', 'Green', 1), ('US', 'Green', 2), ('US', 'Green', 4),
      ('US', 'Green', 5), ('US', 'Green', 6)]
      

      在此之后,结果可以很容易地输入到你的函数中

      【讨论】:

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