首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Pandas Dataframe -带条件/行迭代/上一行计算的最小函数

Pandas Dataframe -带条件/行迭代/上一行计算的最小函数
EN

Stack Overflow用户
提问于 2018-02-27 10:56:25
回答 2查看 718关注 0票数 3

我有一个数据帧,它有一些记录的开始和结束日期:

代码语言:javascript
复制
import pandas as pd

df = pd.DataFrame({'Key': ['A', 'A', 'A', 'A', 'B', 'B', 'B', 'B' ], 
             'StartDate': ['01/01/2015', '01/01/2016', '06/01/2016','10/01/2017', 
                           '01/01/2015', '01/01/2016', '07/15/2016','10/01/2017'], 
               'EndDate': ['12/30/2015', '05/31/2016', '09/30/2017', '12/31/2018', 
                           '12/30/2015', '05/31/2016', '09/30/2017', '12/31/2018']})
df = df[['Key', 'StartDate', 'EndDate']]

print(df)

我的输出如下所示:

代码语言:javascript
复制
 Key   StartDate     EndDate
0   A  01/01/2015  12/30/2015
1   A  01/01/2016  05/31/2016
2   A  06/01/2016  09/30/2017
3   A  10/01/2017  12/31/2018
4   B  01/01/2015  12/30/2015
5   B  01/01/2016  05/31/2016
6   B  07/15/2016  09/30/2017
7   B  10/01/2017  12/31/2018

我需要知道每个密钥的最早开始日期和最晚结束日期。我这样做了(如果有更好的方法,请告诉我):

代码语言:javascript
复制
df_start = df.groupby('Key')['StartDate'].min().reset_index(name = 'StartDate')
df_end = df.groupby('Key')['EndDate'].max().reset_index(name = 'EndDate')

final = pd.merge(df_start, df_end, on = 'Key', how = 'left')
print(final)

这给了我以下输出:

代码语言:javascript
复制
  Key   StartDate     EndDate
0   A  01/01/2015  12/31/2018
1   B  01/01/2015  12/31/2018

现在,如果您查看原始数据帧中的关键字"B“,您将看到第5行的结束日期是2016年5月31日,而第6行的开始日期是2016年7月15日,因此这些记录不是连续的。日期有一个1.5个月的中断。在日期中断超过3天的情况下,我只需要返回连续记录的最早开始日期,因此在这种情况下,期望的输出将是:

代码语言:javascript
复制
Key   StartDate     EndDate
    0   A  01/01/2015  12/31/2018
    1   B  07/15/2016  12/31/2018

我一直在尝试使用'shift‘方法来计算每一行的开始日期和前一行的结束日期之间的天数,但不确定我是否完全正确。或者我应该遍历各行?我的数据框中有数十万条记录。

实现这一目标的最有效方法是什么?谢谢。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-02-27 12:39:45

好的,你需要创建一个标记来定义连续的记录,然后groupby和drop重复:

代码语言:javascript
复制
df['StartDate'] = pd.to_datetime(df['StartDate'])

df['EndDate'] = pd.to_datetime(df['EndDate'])

consec = (df.groupby('Key').apply(lambda x: x.StartDate - x.EndDate.shift(1) >= pd.Timedelta('3 day'))
            .cumsum().reset_index(drop=True))

(df.groupby(['Key',consec])
   .agg({'StartDate':'min','EndDate':'max'})
   .reset_index()
   .drop_duplicates('Key', keep='last')
   .drop('level_1', axis=1))

输出:

代码语言:javascript
复制
  Key  StartDate    EndDate
0   A 2015-01-01 2018-12-31
2   B 2016-07-15 2018-12-31
票数 3
EN

Stack Overflow用户

发布于 2018-02-27 12:43:12

我不是熊猫专家,但我想我有你想要的东西。首先,我将日期转换为日期时间:

代码语言:javascript
复制
df['StartDate'] = pd.to_datetime(df['StartDate'], infer_datetime_format=True)
df['EndDate'] = pd.to_datetime(df['EndDate'], infer_datetime_format=True)
print(df)

结果:

代码语言:javascript
复制
  Key  StartDate    EndDate
0   A 2015-01-01 2015-12-30
1   A 2016-01-01 2016-05-31
2   A 2016-06-01 2017-09-30
3   A 2017-10-01 2018-12-31
4   B 2015-01-01 2015-12-30
5   B 2016-01-01 2016-05-31
6   B 2016-07-15 2017-09-30
7   B 2017-10-01 2018-12-31

然后确定每个组中结束日期和开始日期之间的时间量:

代码语言:javascript
复制
df['Break'] = (df.groupby('Key')
    .apply(lambda d: d['StartDate'] - d['EndDate'].shift(1))
    .reset_index(level=0, name='Break')['Break']
)
print(df)

结果:

代码语言:javascript
复制
  Key  StartDate    EndDate   Break
0   A 2015-01-01 2015-12-30     NaT
1   A 2016-01-01 2016-05-31  2 days
2   A 2016-06-01 2017-09-30  1 days
3   A 2017-10-01 2018-12-31  1 days
4   B 2015-01-01 2015-12-30     NaT
5   B 2016-01-01 2016-05-31  2 days
6   B 2016-07-15 2017-09-30 45 days
7   B 2017-10-01 2018-12-31  1 days

查找中断点高于我们所需的分界点的位置:

代码语言:javascript
复制
cutoff = pd.Timedelta('3 days')
df['Break_above_cutoff'] = df['Break'] > cutoff
print(df)

结果:

代码语言:javascript
复制
  Key  StartDate    EndDate   Break  Break_above_cutoff
0   A 2015-01-01 2015-12-30     NaT               False
1   A 2016-01-01 2016-05-31  2 days               False
2   A 2016-06-01 2017-09-30  1 days               False
3   A 2017-10-01 2018-12-31  1 days               False
4   B 2015-01-01 2015-12-30     NaT               False
5   B 2016-01-01 2016-05-31  2 days               False
6   B 2016-07-15 2017-09-30 45 days                True
7   B 2017-10-01 2018-12-31  1 days               False

然后,我定义此函数来查找从列中包含true的最后一行开始的数据帧部分:

代码语言:javascript
复制
def get_after_last_true(df, colname):
"""Gets the portion of the dataframe starting from the last occurance of 
   True in colname"""
   idx = np.where(df[colname])[0]
   if len(idx) > 0:
       return df.iloc[idx[-1]:]
   else:
       return df

将其应用于组:

代码语言:javascript
复制
trimmed = (df.groupby('Key')
         .apply(lambda d: get_after_last_true(d, 'Break_above_cutoff'))
         .reset_index(drop=True)
      )
print(trimmed)

结果:

代码语言:javascript
复制
  Key  StartDate    EndDate   Break  Break_above_cutoff
0   A 2015-01-01 2015-12-30     NaT               False
1   A 2016-01-01 2016-05-31  2 days               False
2   A 2016-06-01 2017-09-30  1 days               False
3   A 2017-10-01 2018-12-31  1 days               False
4   B 2016-07-15 2017-09-30 45 days                True
5   B 2017-10-01 2018-12-31  1 days               False

然后使用groupby-apply获得EndDate的最大值和StartDate的最小值的元组

代码语言:javascript
复制
result = trimmed.groupby('Key').apply(
    lambda df: (df['StartDate'].min(), df['EndDate'].max())
)
print(result)

结果:

代码语言:javascript
复制
Key
A    (2015-01-01 00:00:00, 2018-12-31 00:00:00)
B    (2016-07-15 00:00:00, 2018-12-31 00:00:00)
dtype: object
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49000384

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档