2017-09-14 3 views
0

나는 시작 날짜 시간 (타임 스탬프)과 선택적인 끝 날짜 시간 (취소 된 경우)이있는 가입의 팬더 데이터 프레임을 가지고 있습니다.날짜 범위가있는 DataFrame에서 팬더 일일 집계 시간 시리즈 만들기

간단히하기 위해 시작 및 종료 날짜 시간 (타임 스탬프)을 기준으로 날짜 (예 : "20170901")의 문자열 열을 만들었습니다. 그것은 다음과 같습니다

df = pd.DataFrame([('20170511', None), ('20170514', '20170613'), ('20170901', None), ...], columns=["sd", "ed"])

최종 결과는 많은 구독 범위에서 주어진 날짜에 활성 얼마나의 시계열해야합니다. 이를 위해

, 나는 범위 내에서 모든 일에 대한 색인을 생성 :

days = df.groupby(["sd"])["sd"].count()

나는 각각 전체 DataFrame를 통해 쿼리를 실행하는 루프에 관심이 무엇을 만들 수 있어요

df. 내가 원래 데이터 집합의 매일의 값을, 그래서 틈이없는

count_by_day = pd.DataFrame([ len(df.loc[(df.sd <= i) & (df.ed.isnull() | (df.ed > i))]) for i in days.index], index=days.index)

참고. 기간을 늘릴 수 있다고 확신합니다.

실제 질문은 : 수천 개의 행이있는 큰 초기 데이터 세트 df에 대해이를 효율적으로 계산할 수 있습니까? 내가 사용한 방법은 복잡성이 2 차적 인 것 같습니다. 또한 df.query()를 시도했지만 Pythonic 필터보다 66 % 느리고 복잡도는 변하지 않습니다.

예를 들어 팬더 문서를 검색하려고했지만 잘못된 키워드를 사용하고있는 것으로 보입니다. 어떤 아이디어?

답변

1

흥미로운 문제는 여기에 있습니다. 어떻게할까요? 내 첫 번째 대답이 잘못했다, 내가 읽어 보지 않았 완전히 질문

# Initial data, columns as Timestamps 
df = pd.DataFrame([('20170511', None), ('20170514', '20170613'), ('20170901', None)], columns=["sd", "ed"]) 
df['sd'] = pd.DatetimeIndex(df.sd) 
df['ed'] = pd.DatetimeIndex(df.ed) 

# Range input and related index 
beg = pd.Timestamp('2017-05-15') 
end = pd.Timestamp('2017-09-15') 
idx = pd.DatetimeIndex(start=beg, end=end, freq='D') 

# We filter data for records out of the range and then clip the 
# the subscriptions start/end to the range bounds. 
fdf = df[(df.sd <= beg) | ((df.ed >= end) | (pd.isnull(df.ed)))] 
fdf['ed'].fillna(end, inplace=True) 
fdf['ps'] = fdf.sd.apply(lambda x: max(x, beg)) 
fdf['pe'] = fdf.ed.apply(lambda x: min(x, end)) 

# We run a conditional count 
idx.to_series().apply(lambda x: len(fdf[(fdf.ps<=x) & (fdf.pe >=x)])) 
+0

감사합니다 ! 코드 패턴 중 일부는 내 질문에있는 코드보다 훨씬 훌륭합니다. – mike921

0

좋아, 내가 연구의 꽤 후에 내 자신의 질문에 대답 조롱하고 노력하고있어 성능

편집 확실하지 않음 밖으로 물건. 나는 여전히 분명한 해결책을 놓치고 있을지 모르지만 아마도 도움이 될 것입니다.

# Start with test data from question 
df = pd.DataFrame([('20170511', None), ('20170514', '20170613'), 
        ('20170901', None), ...], columns=['sd', 'ed']) 

# Convert to datetime columns 
df['sd'] = pd.DatetimeIndex(df['sd']) 
df['ed'] = pd.DatetimeIndex(df['ed']) 
df.ed.fillna(df.sd.max(), inplace=True) 

# Note: In my real data I have timestamps - I convert them like this: 
#df['sd'] = pd.to_datetime(df['start_date'], unit='s').apply(lambda x: x.date()) 

# Set and sort multi-index to enable slices 
df = df.set_index(['sd', 'ed'], drop=False) 
df.sort_index(inplace=True) 

# Compute the active counts by day in range 
di = pd.DatetimeIndex(start=df.sd.min(), end=df.sd.max(), freq='D') 
count_by_day = di.to_series().apply(lambda i: len(df.loc[ 
      (slice(None, i.date()), slice(i.date(), None)), :])) 

내 실제 데이터 세트에서 (> 10K df에 대한 행과 약 1 년의 날짜 범위) : 나는 지금까지 찾을 수있는

가장 빠른 해결책은 (일부 좋은 코드 패턴에 대한 감사 알렉스)입니다 , 이것은 질문의 코드 인 1.5 초에 비해 2 배 빠릅니다. 여기

내가 배운 교훈 :

  • 기간에 대한 카운터와 시리즈를 생성 및 df.apply 또는 df.itertuples와 데이터 세트 df을 반복하고 카운터를 증가 훨씬 느렸다. 흥미롭게도 applyitertuples보다 느립니다.생각하지 말것 iterrows
  • 데이터 세트에 각 행에 product_id가 있으므로 각 제품의 데이터 세트를 필터링하고 필터링 된 결과 (각 제품)에 대한 계산을 실행하는 것이 product_id를 멀티에 추가하는 것보다 두 배 빠릅니다. -index 및 해당 수준에서의 슬라이스
  • 각 행을 반복하여 df에있는 활성 기간의 시리즈를 만들고 활성 범위의 각 날짜를 계열에 추가 한 다음 날짜별로 그룹화하는 것이 훨씬 느립니다.
  • 다중 색인이있는 df의 질문에서 코드를 실행해도 성능이 변경되지 않았습니다.
  • df에서 제한된 열 (실제 데이터 세트가 22 열)을 사용하여 코드를 실행해도 성능이 변경되지 않았습니다.
  • 내가 pd.crosstabpd.Period보고했지만, 난 아무것도
  • 팬더 꽤 굉장 작동시킬 수 없습니다 그것은 하드 정말 현명하려고 (특히. 비 벡터화 파이썬)
관련 문제