Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/276.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 有效地检查dataframe的日期是否介于范围之间,并返回计数_Python_Python 3.x_Pandas_Dataframe_Vectorization - Fatal编程技术网

Python 有效地检查dataframe的日期是否介于范围之间,并返回计数

Python 有效地检查dataframe的日期是否介于范围之间,并返回计数,python,python-3.x,pandas,dataframe,vectorization,Python,Python 3.x,Pandas,Dataframe,Vectorization,假设我们有一个数据框df,其中有一个日期列表,按日期的时间顺序排列 目标是获取给定日期内日期范围包括给定日期的人数 df=pd.DataFrame(数据={'date':[datetime.date(2007,12,1), 日期时间日期(2007年12月2日), 日期时间.日期(2007年12月3日)], 'num_people_on_day':[0,0,0]}) dg=pd.DataFrame(数据={'person':['Alice','Bob','Chuck'], “开始”:[dateti

假设我们有一个数据框df,其中有一个日期列表,按日期的时间顺序排列

目标是获取给定日期内日期范围包括给定日期的人数

df=pd.DataFrame(数据={'date':[datetime.date(2007,12,1),
日期时间日期(2007年12月2日),
日期时间.日期(2007年12月3日)],
'num_people_on_day':[0,0,0]})
dg=pd.DataFrame(数据={'person':['Alice','Bob','Chuck'],
“开始”:[datetime.date(2007年11月5日),
日期时间日期(2007年12月8日),
日期时间.日期(2007年1月5日)],
"end":[datetime.date(2007,12,6),,
日期时间.日期(2008年1月3日),
datetime.date(2007年11月30日)}

因此,对于
df
中的每个日期,我如何有效地检查所有
dg
,然后计算返回的数字并将其放入
df

%%timeit 
m = df[['date']].assign(k=1).merge(dg.assign(k=1))
(m[m.date.between(m.start, m.end)].groupby('date').size()
   .reindex(df.date).fillna(0)
   .rename('num_people_on_day').reset_index())
#9.39 ms ± 52 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%%timeit 
dg.index = pd.IntervalIndex.from_arrays(dg['start'], dg['end'], closed='both')
df['num_people_on_day'] = df['date'].apply(get_len, dg=dg)
#4.06 ms ± 27.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
我甚至不确定这里是否需要合并(同时也试图节省内存),我真的在尽可能快地编写它

编辑:好的,所以我想出了一种不同的方法,但我讨厌使用apply。有没有一种不使用.apply的新方法

import pandas as pd
import datetime

df = pd.DataFrame(data={'date': [datetime.date(2007, 12, 1), 
                                 datetime.date(2007, 12, 2), 
                                 datetime.date(2007, 12, 3)]})

dg = pd.DataFrame(data={'person': ['Alice', 'Bob', 'Chuck', 'Dave'],
                        'start': [datetime.date(2007, 11, 5), 
                                  datetime.date(2007, 12, 8), 
                                  datetime.date(2007, 1, 5),
                                  datetime.date(2007, 11, 6)],
                        'end': [datetime.date(2007, 12, 1), 
                                datetime.date(2008, 1, 3), 
                                datetime.date(2007, 11, 30),
                                datetime.date(2007, 12, 2)]})

def get_num_persons(date, vec_starts, vec_ends):
    """
    Helper function for .apply to get number of persons.
    For each given date, if start and end date is 
    between the given date, then both results are True.
    The bitwise AND then only sums these True and True values.
    """
    return (((vec_starts <= date) & (vec_ends >= date)).sum())

def num_of_persons(persons, dates_df):
    """
    Obtains the number of persons for each day.
    """
    dates_df['num_persons'] = dates_df['date'].apply(lambda row: 
                                                   get_num_persons(row, 
                                                   persons['start'],
                                                   persons['end']))
    return dates_df

num_of_persons(dg, df.copy())
将熊猫作为pd导入
导入日期时间
df=pd.DataFrame(data={'date':[datetime.date(2007,12,1)),
日期时间日期(2007年12月2日),
datetime.date(2007年12月3日)}
dg=pd.DataFrame(数据={'person':['Alice','Bob','Chuck','Dave'],
“开始”:[datetime.date(2007年11月5日),
日期时间日期(2007年12月8日),
日期时间日期(2007年1月5日),
日期时间.日期(2007年11月6日)],
"end":[datetime.date(2007,12,1),,
日期时间.日期(2008年1月3日),
日期时间日期(2007年11月30日),
datetime.date(2007年12月2日)}
def get_num_人员(日期、vec_开始、vec_结束):
"""
的助手函数。应用以获取人数。
对于每个给定日期,如果开始和结束日期为
在给定日期之间,则两个结果均为真。
按位AND then仅对这些真值和真值求和。
"""
返回(((vec_start=date)).sum()
定义人数(人员、日期):
"""
获取每天的人数。
"""
日期_df['num_persons']=日期_df['date']。应用(lambda行:
获取人数(世界其他地区),
人员[“开始”],
人员['end']))
返回日期
人数(dg、df.copy())

有足够的内存,
合并
然后计算介于两者之间的日期
.reindex
确保我们得到0

#df['date'] = pd.to_datetime(df.date)
#dg['start'] = pd.to_datetime(dg.start)
#dg['end'] = pd.to_datetime(dg.end)

m = df[['date']].assign(k=1).merge(dg.assign(k=1))

(m[m.date.between(m.start, m.end)].groupby('date').size()
   .reindex(df.date).fillna(0)
   .rename('num_people_on_day').reset_index())

         date  num_people_on_day
0  2007-12-01                  1
1  2007-12-02                  1
2  2007-12-03                  1

另一个选项是使用apply。这是一个循环,因此随着
df
的增长,性能会受到影响

def get_len(x, dg):
    try:
        return len(dg.iloc[dg.index.get_loc(x)])
    except KeyError:  # Deal with dates that have 0
        return 0

dg.index = pd.IntervalIndex.from_arrays(dg['start'], dg['end'], closed='both')
df['num_people_on_day'] = df['date'].apply(get_len, dg=dg)

要说明计时,请查看您的小集合,然后查看更大的
df

%%timeit 
m = df[['date']].assign(k=1).merge(dg.assign(k=1))
(m[m.date.between(m.start, m.end)].groupby('date').size()
   .reindex(df.date).fillna(0)
   .rename('num_people_on_day').reset_index())
#9.39 ms ± 52 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%%timeit 
dg.index = pd.IntervalIndex.from_arrays(dg['start'], dg['end'], closed='both')
df['num_people_on_day'] = df['date'].apply(get_len, dg=dg)
#4.06 ms ± 27.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
但一旦
df
变长(即使只有90行),差异就会变得明显

df = pd.DataFrame({'date': pd.date_range('2007-01-01', '2007-03-31')})

%%timeit merge
#9.78 ms ± 75.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%%timeit IntervalIndex
#65.5 ms ± 418 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

或者也许或者。然后只需要一个群组和大小来获得计数。有趣但不是真的。最后两个链接没有用处。也许是第一个,但我这里不分类。就像我说的,我甚至不认为我需要合并。我将尝试修复案例的第一个链接。最后两个链接非常有用:
m=df.assign(k=1)。merge(dg.assign(k=1))
然后是
m[m.date.between(m.start,m.end)]。groupby('date')。size()
可以100%解决您的问题(可能添加
.reindex(df.date)。fillna(0)
,但这并不是问题的焦点)你是对的,它可以工作,但是对我来说,合并产生了太多的重复,因此我得到了一个MemoryError(1200天的appx 50k行->6000万行)。需要研究另一种方法,谢谢。同意计算是有效的。但是由于合并,内存效率很低。@bbd108是的,我同意。这些类型的计算似乎总是在时间和内存之间进行权衡。