在Python中对一段日期进行分组

3

我有以下数据库,我想要将PAS_DATE按照3天的范围进行分组,例如从2015-03-06到2015-03-09。

PAS_DATE    RED_DATE       TOT
2015-03-06  2015-03-07     2
            2015-03-17    14
            2015-12-22     1
2015-03-07  2015-03-08     3
            2015-03-19     6
            2015-10-14     2
            2015-12-07     1
2015-03-08  2015-09-16     8
2015-03-09  2015-03-09     7
            2015-03-15     6
            2015-03-18     8
            2015-04-04    15
            2015-04-12    19
            2015-05-04    44
            2015-08-17     5
            2015-09-09    13
            2015-12-06     3
            2015-12-13     3
2015-03-10  2015-03-10     7

输出应该是:
PAS_DATE    RED_DATE      TOT
2015-03-09  2015-03-07     2
            2015-03-17    14
            2015-12-22     1
            2015-03-08     3
            2015-03-19     6
            2015-10-14     2
            2015-12-07     1
            2015-09-16     8
            2015-03-09     7
            2015-03-15     6
            2015-03-18     8
            2015-04-04    15
            2015-04-12    19
            2015-05-04    44
            2015-08-17     5
            2015-09-09    13
            2015-12-06     3
            2015-12-13     3
2015-03-12  2015-03-10     7

有没有一种在Pandas或Python中不使用迭代的方法来执行此代码?

不使用迭代结构?你是指完全不使用任何循环结构吗? - tlastowka
是的,我有一个大数据集,循环结构很慢... - user3333155
不管怎样,您都需要迭代行来评估数据。循环是任何基于集合或编程的核心结构之一。 - tlastowka
我尝试了您有趣的解决方案,但是我得到了一个 ITypeError 错误,错误代码为 df ['for_group_by'] = df.PAS_DATE + df['shift']:ufunc add 无法使用类型为 dtype('O') 和 dtype('< m8 [ns]') 的操作数。 - user3333155
我已经更新了我的原始答案。 - user2532323
1个回答

0

我不知道这是否比使用iterrows()循环更有效,但这里有一种方法(虽然有点hacky):

df = pd.DataFrame(pd.date_range("2015-01-01","2015-01-10"), columns=['PAS_DATE'])

df['dt_since_first'] = (df.PAS_DATE - df.PAS_DATE.min()).dt.days
df['shift'] = pd.to_timedelta(2 - np.mod(df.dt_since_first, 3), unit='d')
df['for_group_by'] = df.PAS_DATE + df['shift']
  1. 创建一个新列,保存自第一个日期以来的时间差,并将其转换为整数,即天数。
  2. 现在使用mod3找出PAS_DATE必须移动多少天才能到达您的3天(ceil)分组,并将其转换回timedelta对象。
  3. 只需按此timedelta移动PAS_DATE即可。

以下是输出:

    PAS_DATE  dt_since_first  shift for_group_by
0 2015-01-01               0 2 days   2015-01-03
1 2015-01-02               1 1 days   2015-01-03
2 2015-01-03               2 0 days   2015-01-03
3 2015-01-04               3 2 days   2015-01-06
4 2015-01-05               4 1 days   2015-01-06
5 2015-01-06               5 0 days   2015-01-06
6 2015-01-07               6 2 days   2015-01-09
7 2015-01-08               7 1 days   2015-01-09
8 2015-01-09               8 0 days   2015-01-09
9 2015-01-10               9 2 days   2015-01-12

您可以在同一列上进行迭代,我只是为了更容易理解,每个步骤都使用了不同的列...

评论编辑

我猜您的列PAS_DATE的dtype为object,尽管您的条目的dtype为np.datetime[64]。有一些强烈的理由,为什么应该避免这种情况。

我曾经在一个非常长的DataFrame中遇到过这种情况,直到我找出问题所在,我才度过了地狱般的日子。出现了各种异常,我在Stackoverflow上花费了无数小时来解决我的问题。此外,所有东西都相当缓慢,最重要的是,保存df所需的内存需求非常大。

发生了什么。您的列的dtype为object,这意味着它仅保存指向大量datetime对象的指针。如果它的dtype为np.datetime[64],它将简单地保存64位的datetime信息。

尝试查看信息,它应该像这样:

print df.info()

Output:
<class 'pandas.core.frame.DataFrame'>
Int64Index: 10 entries, 0 to 9
Data columns (total 4 columns):
PAS_DATE          10 non-null datetime64[ns]
dt_since_first    10 non-null int64
shift             10 non-null timedelta64[ns]
for_group_by      10 non-null datetime64[ns]
dtypes: datetime64[ns](2), int64(1), timedelta64[ns](1)
memory usage: 400.0 bytes

现在要转换您的PAS_DATE列,请尝试以下方法:

df.PAS_DATE = df.PAS_DATE.convert_objects(convert_dates='coerce')

顺便说一下,你应该尽快在代码中执行此操作。而且可能也应该对其他日期列执行此操作。在转换之前和之后执行df.info()。告诉我们内存使用情况的差异是什么。

这不是你的代码问题。Pandas 是处理这种情况的好方法,但它仍然在迭代。现在迭代是在 Pandas 包中进行的,以将数据映射到索引数据帧中。“无需迭代”这个想法从根本上是有缺陷的。在某些时候,数据集必须被拆分成行,并且每一行都必须以某种方式进行评估,无论是通过其实际内容还是通过其与其他行的元数据相关性。 - tlastowka
@tlastowka 您是正确的。当然,由于相同的操作会多次执行(字面上的for each行),因此肯定会有循环。实际上有几个循环,因为我的代码需要多次迭代数据框。尽管如此,现在已经向量化了,numpy以高度优化的方式执行操作。我想这样更快,但也需要更多的内存。 - user2532323

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接