【问题标题】:Identify periods in a pandas series where several consecutive values are negative识别熊猫系列中多个连续值为负数的时期
【发布时间】:2021-07-03 01:39:54
【问题描述】:

给定一个以年份为索引的 pandas 系列(按升序排列,没有缺失年份):

growth = pd.Series({1990: 6.99, 1991: 5.53, 1992: -9.02, 1993: 1.05, 1994: 9.24, 1995: 0.16, 1996: 10.36, 1997: 2.68, 1998: 2.89, 1999: -0.82, 2000: -3.06, 2001: 1.44, 2002: -8.89, 2003: -17.0, 2004: -5.81, 2005: -5.71, 2006: -3.46, 2007: -3.65, 2008: -17.67, 2009: 12.02, 2010: 19.68, 2011: 14.19, 2012: 16.67, 2013: 1.99, 2014: 2.38, 2015: 1.78, 2016: 0.76, 2017: 4.7, 2018: 3.5, 2019: -8.1, 2020: -8.0})

我需要确定growth 至少连续几年为负数的时期(开始和结束年份)。

我可以通过遍历系列来做到这一点:

def get_negative_periods(s, min_duration):
    previous = 1
    negative_periods = []
    for year, value in s.items():
        if value < 0:
            if previous < 0:
                negative_periods[-1].append(year)
            else:
                negative_periods.append([year])
        previous = value
    return [(period[0], period[-1]) for period in negative_periods
        if len(period) >= min_duration]

例如get_negative_periods(growth, 3) 返回 [(2002, 2008)],因为 2002-2008 年是唯一一个 growth 连续 3 年或更长时间为负数的时期。

有没有办法将其矢量化而不是逐行进行? (返回一个系列或数据框而不是元组就可以了。)

【问题讨论】:

    标签: python pandas vectorization


    【解决方案1】:

    尝试根据TrueFalse 不同的地方创建组,然后只保留年份范围大于或等于min_duration 的True 组:

    def get_negative_periods(s, min_duration):
        s = s.lt(0).reset_index()
        g = s[0].ne(s[0].shift()).cumsum()[s[0].eq(True)]
        s = s.groupby(g)['index'].agg(['first', 'last'])
        return s[(s['last'] - s['first']) + 1 >= min_duration]
    
    
    res = get_negative_periods(growth, 3)
    

    res:

         first  last
    0               
    6.0   2002  2008
    

    或作为列表列表:

    def get_negative_periods(s, min_duration):
        s = s.lt(0).reset_index()
        g = s[0].ne(s[0].shift()).cumsum()[s[0].eq(True)]
        s = s.groupby(g)['index'].agg(['first', 'last'])
        return s[(s['last'] - s['first']) + 1 >= min_duration].values.tolist()
    
    
    lst = get_negative_periods(growth, 3)
    

    lst:

    [[2002, 2008]]
    

    【讨论】:

      【解决方案2】:

      这是另一种方式:

      min_duration = 3
      
      (s.rename_axis('year').lt(0).diff().ne(0).cumsum()
       .where(s.lt(0)).reset_index(name='cc')
       .groupby('cc').agg(start = ('year','first'),end = ('year','last'))
       .loc[lambda x: (x['end']-x['start']).gt(min_duration)])
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-08-11
        • 1970-01-01
        • 2019-03-04
        • 2022-08-17
        • 1970-01-01
        相关资源
        最近更新 更多