【问题标题】:Filter Dates in Pandas在 Pandas 中过滤日期
【发布时间】:2019-03-11 15:43:55
【问题描述】:

目前有一个数据集结构如下:

id_number    start_date    end_date   data1    data2    data3   ...

基本上,我有一大堆具有特定日期范围的 id,然后是多列摘要数据。我的问题是我需要汇总数据的年度总数。这意味着我需要到达一个可以按年份对每个文档的一次出现进行分组的地方。但是,不能保证给定年份的文档存在,并且日期范围可以跨越多年。任何帮助将不胜感激,我很困惑。

示例数据框:

df = pd.DataFrame([[1, '3/10/2002', '4/12/2005'], [1, '4/13/2005', '5/20/2005'], [1, '5/21/2005', '8/10/2009'], [2, '2/20/2012', '2/20/2015'], [3, '10/19/2003', '12/12/2012']])
df.columns = ['id_num', 'start', 'end']
df.start = pd.to_datetime(df['start'], format= "%m/%d/%Y")
df.end = pd.to_datetime(df['end'], format= "%m/%d/%Y")

【问题讨论】:

  • 如果记录跨越一年以上,这对总数意味着什么? / 你想怎么对付他们? (或者这是你的问题?)
  • 查看您的数据我想知道您将如何区分不同年份的数据。例如,id_number 43482 的 start_date 为 2017 年 2 月 3 日,end_date 为 2019 年 3 月 10 日,data1 为 119。你怎么知道 2018 年的数据是什么?我需要更多信息。
  • 当然,所以如果它跨越多年,那么该数据在跨越的年份应该保持不变。因此,如果一个 id 跨越 2005-2007 年,然后在 2008 年发生变化,那么 2005 年、2006 年和 2007 年的数据应该相同,然后在 2008 年发生变化。
  • MacItaly,这里的假设是如果有意义的话,数据在每个条目的时间段内保持不变。
  • 如果您可以提供带有pd.DataFrame(...) 的示例数据框,将更容易为我们解答:stackoverflow.com/questions/20109391/…

标签: python pandas date datetime


【解决方案1】:

假设我们有一个 DataFrame df:

   id_num      start        end  value
0       1 2002-03-10 2005-04-12      1
1       1 2005-04-13 2005-05-20      2
2       1 2007-05-21 2009-08-10      3
3       2 2012-02-20 2015-02-20      4
4       3 2003-10-19 2012-12-12      5

我们可以为 startend 范围内的每一年创建一行:

ys = [np.arange(x[0], x[1]+1) for x in zip(df['start'].dt.year, df['end'].dt.year)]

df = (pd.DataFrame(ys, df.index)
     .stack()
     .astype(int)
     .reset_index(1, True)
     .to_frame('year')
     .join(df, how='left')
     .reset_index())

print(df)

在这里,我们首先创建 ys 变量,其中包含 DataFrame 中每个 start-end 范围的年份列表,df = ... 将这些年份列表拆分为单独的行并连接回原始 DataFrame(与本文中所做的非常相似:How to convert column with list of values into rows in Pandas DataFrame)。

输出:

    index  year  id_num      start        end  value
0       0  2002       1 2002-03-10 2005-04-12      1
1       0  2003       1 2002-03-10 2005-04-12      1
2       0  2004       1 2002-03-10 2005-04-12      1
3       0  2005       1 2002-03-10 2005-04-12      1
4       1  2005       1 2005-04-13 2005-05-20      2
5       2  2007       1 2007-05-21 2009-08-10      3
6       2  2008       1 2007-05-21 2009-08-10      3
7       2  2009       1 2007-05-21 2009-08-10      3
8       3  2012       2 2012-02-20 2015-02-20      4
9       3  2013       2 2012-02-20 2015-02-20      4
10      3  2014       2 2012-02-20 2015-02-20      4
11      3  2015       2 2012-02-20 2015-02-20      4
12      4  2003       3 2003-10-19 2012-12-12      5
13      4  2004       3 2003-10-19 2012-12-12      5
14      4  2005       3 2003-10-19 2012-12-12      5
15      4  2006       3 2003-10-19 2012-12-12      5
16      4  2007       3 2003-10-19 2012-12-12      5
17      4  2008       3 2003-10-19 2012-12-12      5
18      4  2009       3 2003-10-19 2012-12-12      5
19      4  2010       3 2003-10-19 2012-12-12      5
20      4  2011       3 2003-10-19 2012-12-12      5
21      4  2012       3 2003-10-19 2012-12-12      5

注意: 我将原始范围更改为测试用例,其中一些 id_num 缺少一些年份,例如对于id_num=1,我们有年份2002-20052005-20052007-2009,所以我们不应该在输出中得到2006 for id_num=1(我们没有,所以它通过了测试)

【讨论】:

  • 非常感谢!
【解决方案2】:

我以你的例子为例子并添加了一些随机值,以便我们可以使用:

df = pd.DataFrame([[1, '3/10/2002', '4/12/2005'], [1, '4/13/2005', '5/20/2005'], [1, '5/21/2005', '8/10/2009'], [2, '2/20/2012', '2/20/2015'], [3, '10/19/2003', '12/12/2012']])
df.columns = ['id_num', 'start', 'end']
df.start = pd.to_datetime(df['start'], format= "%m/%d/%Y")
df.end = pd.to_datetime(df['end'], format= "%m/%d/%Y")

np.random.seed(0)  # seeding the random values for reproducibility
df['value'] = np.random.random(len(df))

到目前为止,我们有:

    id_num  start   end     value
0   1   2002-03-10  2005-04-12  0.548814
1   1   2005-04-13  2005-05-20  0.715189
2   1   2005-05-21  2009-08-10  0.602763
3   2   2012-02-20  2015-02-20  0.544883
4   3   2003-10-19  2012-12-12  0.423655

我们想要每个给定日期在年底的值,无论是开始还是结束。因此,我们将所有日期一视同仁。我们只想要日期 + 用户 + 价值:

tmp = df[['end', 'value']].copy()
tmp = tmp.rename(columns={'end':'start'})
new = pd.concat([df[['start', 'value']], tmp], sort=True)
new['id_num'] = df.id_num.append(df.id_num)  # doubling the id numbers

给我们:

    start      value    id_num
0   2002-03-10  0.548814    1
1   2005-04-13  0.715189    1
2   2005-05-21  0.602763    1
3   2012-02-20  0.544883    2
4   2003-10-19  0.423655    3
0   2005-04-12  0.548814    1
1   2005-05-20  0.715189    1
2   2009-08-10  0.602763    1
3   2015-02-20  0.544883    2
4   2012-12-12  0.423655    3

现在我们可以按 ID 号和年份分组:

new = new.groupby(['id_num', new.start.dt.year]).sum().reset_index(0).sort_index()

    id_num  value
start       
2002    1   0.548814
2003    3   0.423655
2005    1   2.581956
2009    1   0.602763
2012    2   0.544883
2012    3   0.423655
2015    2   0.544883

最后,对于每个用户,我们将范围扩大到每年之间,填补缺失的数据:

new = new.groupby('id_num').apply(lambda x: x.reindex(pd.RangeIndex(x.index.min(), x.index.max() + 1)).fillna(method='ffill')).drop(columns='id_num')

             value
id_num      
1   2002    0.548814
    2003    0.548814
    2004    0.548814
    2005    2.581956
    2006    2.581956
    2007    2.581956
    2008    2.581956
    2009    0.602763
2   2012    0.544883
    2013    0.544883
    2014    0.544883
    2015    0.544883
3   2003    0.423655
    2004    0.423655
    2005    0.423655
    2006    0.423655
    2007    0.423655
    2008    0.423655
    2009    0.423655
    2010    0.423655
    2011    0.423655
    2012    0.423655

【讨论】:

  • 我认为这里有问题。我从我的回答中在 DataFrame 上检查了这一点,即使没有包含 year=2006id_num=1 的记录,我也得到了 1 2006 5.0 的代码
  • 我在让代码也能正常工作时遇到了麻烦,但这个想法是正确的,并且在修改代码时效果很好。非常感谢!
  • 不客气。它似乎有一个错误,虽然我不完全确定在哪里。如果您让它以修改后的方式工作,请随时编辑我的答案。
猜你喜欢
  • 2014-05-18
  • 2016-08-21
  • 2017-09-06
  • 2020-03-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多