有效地找到许多日期范围之间的重叠

时间:2016-02-08 09:43:22

标签: python pandas

如何有效地找到许多日期范围之间的重叠日期?

我有一个pandas数据框,其中包含许多产品的每日仓库库存信息。那些股票实际发生变化的日期只有记录。

import pandas as pd
df = pd.DataFrame({'product': ['a', 'a', 'a', 'b', 'b', 'b'],
                  'stock': [10, 0, 10, 5, 0, 5],
                  'date': ['2016-01-01', '2016-01-05', '2016-01-15',
                          '2016-01-01', '2016-01-10', '2016-01-20']})
df['date'] = pd.to_datetime(df['date'])
Out[4]: 
        date product  stock
0 2016-01-01       a     10
1 2016-01-05       a      0
2 2016-01-15       a     10
3 2016-01-01       b      5
4 2016-01-10       b      0
5 2016-01-20       b      5

根据这些数据,我想确定所有产品的库存天数为0.在这个例子中,这将是5天(从2016-01-10到2016-01-14) )。

我最初尝试重新采样日期,以便每天创建一条记录,然后逐日比较。这可以工作,但它会创建一个非常大的数据帧,我几乎无法保留在内存中,因为我的数据包含许多库存不会改变的日期。

除了为每个日期创建记录并逐日比较之外,是否有一种更节省内存的方法来计算重叠?

也许我可以某种方式为每个记录中隐含的时间范围创建一个句点表示,然后比较所有产品的所有时间段? 另一种选择可以是首先仅对产品零库存(相对较少)的时间段进行子集,然后仅对该数据子集应用重采样。 还有哪些更有效的方法?

2 个答案:

答案 0 :(得分:1)

您可以使用日期作为索引并将产品作为列来旋转表格,然后使用以前的值填充nan,转换为每日频率并查找所有列中包含0&#39s的行。

ptable = (df.pivot(index='date', columns='product', values='stock')
          .fillna(method='ffill').asfreq('D', method='ffill'))
cond = ptable.apply(lambda x: (x == 0).all(), axis='columns')
print(ptable.index[cond])

DatetimeIndex(['2016-01-10', '2016-01-11', '2016-01-12', '2016-01-13',
               '2016-01-14'],
              dtype='datetime64[ns]', name=u'date', freq='D')

答案 1 :(得分:0)

在此尝试此操作,我知道它不是最漂亮的代码,但根据此处提供的所有数据,应该工作:

from datetime import timedelta
import pandas as pd

df = pd.DataFrame({'product': ['a', 'a', 'a', 'b', 'b', 'b'],
                   'stock': [10, 0, 10, 5, 0, 5],
                   'date': ['2016-01-01', '2016-01-05', '2016-01-15',
                            '2016-01-01', '2016-01-10', '2016-01-20']})
df['date'] = pd.to_datetime(df['date'])
df = df.sort('date', ascending=True)
no_stock_dates = []
product_stock = {}
in_flag = False
begin = df['date'][0]
for index, row in df.iterrows():
    current = row['date']
    product_stock[row['product']] = row['stock']
    if current > begin:
        if sum(product_stock.values()) == 0 and not in_flag:
            in_flag = True
            begin = row['date']
        if sum(product_stock.values()) != 0 and in_flag:
            in_flag = False
            no_stock_dates.append((begin, current-timedelta(days=1)))

print no_stock_dates

此代码应在O(n * k)处运行,其中n是行数,k是产品类别的数量。