计算多个范围内的天数

时间:2015-08-09 22:07:30

标签: python datetime pandas

我有代表范围的行(from - > to)。这是数据的一个子集。

df = DataFrame({'from': ['2015-08-24','2015-08-24'], 'to': ['2015-08-26','2015-08-31']})

         from          to
0  2015-08-24  2015-08-26
1  2015-08-24  2015-08-31

我想计算范围内每天的营业日数。这是我的代码。

# Creating a business time index by taking min an max values from the ranges
b_range = pd.bdate_range(start=min(df['from']), end=max(df['to']))
# Init of a new DataFrame with this index and the count at 0
result = DataFrame(0, index=b_range, columns=['count'])
# Iterating over the range to select the index in the result and update the count column
for index, row in df.iterrows():
    result.loc[pd.bdate_range(row['from'],row['to']),'count'] += 1
print(result)

            count
2015-08-24      2
2015-08-25      2
2015-08-26      2
2015-08-27      1
2015-08-28      1
2015-08-31      1

它有效,但是有没有人知道更多的pythonic方式(即没有for循环)?

3 个答案:

答案 0 :(得分:2)

警告,我有点讨厌这个答案,但是在这个微小的数据框架上它的速度提高了2倍以上,所以我会把它作为一个可行的,如果不是优雅的替代方案扔掉那里。

setFocusPolicy(Qt::TabFocus);

基本上我在这里做的就是创建一个包含所有日期的列,然后使用numpy df2 = df.apply( lambda x: [ pd.bdate_range( x['from'], x['to'] ) ], axis=1 ) arr = np.unique( np.hstack( df2.values ), return_counts=True ) result = pd.DataFrame( arr[1], index=arr[0] ) (类似于pandas unique)来添加所有内容。我希望得到一些更优雅和可读的东西,但这就是我现在拥有的东西。

答案 1 :(得分:2)

以下是使用cumsum()的方法。如果你有很多范围,它应该比for循环更快:

import pandas as pd
df = pd.DataFrame({
        'from': ['2015-08-24','2015-08-24'], 
        'to': ['2015-08-26','2015-08-31']})

df = df.apply(pd.to_datetime)

from_date = min(df['from'])
to_date = max(df['to'])
b_range = pd.bdate_range(start=from_date, end=to_date)
d_range = pd.date_range(start=from_date, end=to_date)

s = pd.Series(0, index=d_range)
from_count = df["from"].value_counts()
to_count = df["to"].value_counts()
s.add(from_count, fill_value=0).sub(to_count.shift(freq="D"), fill_value=0).cumsum().reindex(b_range)

答案 2 :(得分:0)

我对这些解决方案并不完全满意。所以我一直在寻找,我想我找到了一个相当优雅和快速的解决方案。 它的灵感来自于" Pivoting' long'广泛的'格式"在Wes McKinney的书中解释: Python for Data Analysis

我在代码中添加了很多注释,但我认为最好打印出每一步来理解它。

df = DataFrame({'from': ['2015-08-24','2015-08-24'], 'to': ['2015-08-26','2015-08-31']})
# Convert boundaries to datetime
df['from'] = pd.to_datetime(df['from'], format='%Y-%m-%d')
df['to'] = pd.to_datetime(df['to'], format='%Y-%m-%d')
# Reseting index to create a row id named index
df = df.reset_index(level=0)
# Pivoting data to obtain 'from' as row index and row id ('index') as column, 
# each cell cointaining the 'to' date
# In consequence each range (from - to pair) is split into as many columns.
pivoted = df.pivot('from', 'index', 'to')
# Reindexing the table with a range of business dates (i.e. working days)
pivoted = pivoted.reindex(index=pd.bdate_range(start=min(df['from']), 
                                               end=max(df['to'])))
# Filling the NA values forward to copy the to date
# now each row of each column contains the corresponding to date
pivoted = pivoted.fillna(method='ffill')
# Computing the basically 'from' - 'to' for each column and each row and converting the result in days 
# to obtain the number of days between the date in the index and the 'to' date
# Note: one day is added to include the right side of the interval
pivoted = pivoted.apply(lambda x: (x + Day() - x.index) / np.timedelta64(1, 'D'), 
                        axis=0)
# Clipping value lower than 0 (not in the range) to 0
# and values upper than 0 to 1 (only one by day and by id)
pivoted = pivoted.clip_lower(0).clip_upper(1)
# Summing along the columns and that's it
pivoted.sum(axis=1)