Python 在几个范围内计算天数

Python 在几个范围内计算天数,python,datetime,pandas,Python,Datetime,Pandas,我有代表范围的行(从->到)。下面是数据的一个子集 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

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

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

它是有效的,但是有人知道一种更为通俗的方法吗(即没有
for
循环)?

警告,我有点讨厌这个答案,但在这个小数据帧上,它的速度快了2倍多,所以我将把它作为一个可行的,如果不优雅的替代方案扔出去

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] )

基本上,我在这里所做的就是创建一个包含所有日期的列,然后使用numpy
unique
(模拟熊猫
value\u计数
)将所有内容相加。我希望能想出一些更优雅、可读性更强的东西,但这正是我目前拥有的。

这里有一个使用
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)

我对这些解决方案并不完全满意。所以我一直在寻找,我想我找到了一个相当优雅和快速的解决方案。 它的灵感来源于韦斯·麦金尼(Wes McKinney)的《数据分析用Python》一书中解释的“将“长”格式转为“宽”格式”一节

我在我的代码中放了很多注释,但我认为最好打印出每个步骤来理解它

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)

非常快速和干净的解决方案,它的工作很好,我会使用它。谢谢。坦克,但它无法用实际的6361行计算结果。我的iPython笔记本冻结。对不起,这是我的错,这不是因为日期转换。我使用的是一个更大的
数据框
,其中包含其他列。在这种情况下,使用
apply
函数是个坏主意。经过这次修正后,效果很好,我在更短的时间内获得了相同的结果。谢谢你的解答和回复。当然,没问题。正如我承认的,这不是最漂亮的解决方案,但它应该相当快速和可靠。感谢您的跟进。这很奇怪,在我拥有7361行数据集的小型基准测试中,我获得了以下结果:-初始解9.31 s-约翰解3.123 s-最后一个解0.431 s-HYRY解0.077 s我无法与HYRY解相兼容。非常感谢你在这个问题上所做的一切工作。研究这些答案对我来说非常有趣。嗯,这些结果令我惊讶,但我只对问题中提供的样本数据帧进行了计时,这可能无法推广到更大(和不同)的数据帧,因此我删除了之前的评论。谢谢你的跟进。