2016-11-04 39 views
0

我有一個簡單的Pandasdataframe其中每行表示一個人和一個日期範圍。對於每個人,我想知道在dataframe的各個條目中涵蓋硬編碼範圍中的天數百分比(由變量period_start和定義)。通過Pandas函數計算某個範圍內覆蓋的天數百分比

我認爲有一個簡單的方法可以用Pandas來做到這一點,但我一直沒有找到。我有一個解決方案與多個dataframes和幾個嵌套循環,但這是規模效率低下。我怎樣才能更有效地利用Pandas?我認爲groupby是合理的,但不知道如何做到這一點,當範圍跨越兩列,並可能重疊。

import pandas as pd 
from datetime import datetime 
df = pd.DataFrame(data=[['2016-01-01', '2016-01-31', 'A'], 
         ['2016-02-02', '2016-02-10', 'A'], 
         ['2016-03-01', '2016-04-01', 'A'], 
         ['2016-01-01', '2016-03-01', 'B']], 
        columns=['startdate', 'enddate', 'person']) 

# start and end date 
period_start = datetime(year=2016, month=01, day=01) 
period_end = datetime(year=2016, month=12, day=31) 

# dates_dfculate totals days 
total_days = (period_end-period_start).days + 1 

# convert columns to dates 
df['startdate']= pd.to_datetime(df['startdate'], format='%Y-%m-%d') 
df['enddate']= pd.to_datetime(df['enddate'], format='%Y-%m-%d') 

# create a TimeIndex dataframe with columns for each person 
rng = pd.date_range(period_start, periods=total_days, freq='D') 
people = list(set(df['person'].tolist())) 
dates_df = pd.DataFrame(columns=[people], index=rng).fillna(False) 

# loop over each date (index) 
for index, row in dates_df.iterrows(): 

    # loop over each column (person) 
    for person in people: 
     tmp = df[df['person'] == person] 

     # loop over each each entry for the person 
     for index1, row1 in tmp.iterrows(): 

      # check if the date is date index in dates_df is within range 
      value = row1['startdate'] <= index <= row1['enddate'] 

      # if it's not already set to true, set it to true 
      if dates_df.ix[index, person] == False and value == True: 
       dates_df.ix[index, person] = True 

# for each person, show the percentage of days in range that are covered 
for person in people: 
    print person, sum(dates_df[person].tolist())/float(total_days) 

所需的輸出:

A 0.196721311475 
B 0.166666666667 
+0

你期望你的期望輸出到***看起來像***? – Abdou

+0

這只是循環播放每個人並打印出百分比。添加輸出到問題。 – user2242044

回答

1

這應該是吧,我猜,因爲你加1,要具有包容性的範圍內的總天數,而是根據需要編輯它:)

import pandas as pd 
from datetime import datetime 

df = pd.DataFrame(data=[['2016-01-01', '2016-01-31', 'A'], 
         ['2016-02-02', '2016-02-10', 'A'], 
         ['2016-03-01', '2016-04-01', 'A'], 
         ['2016-01-01', '2016-03-01', 'B']], 
        columns=['startdate', 'enddate', 'person']) 

# start and end date 
period_start = datetime(year=2016, month=1, day=1) 
period_end = datetime(year=2016, month=12, day=31) 

# convert columns to dates 
df['startdate']= pd.to_datetime(df['startdate'], format='%Y-%m-%d') 
df['enddate']= pd.to_datetime(df['enddate'], format='%Y-%m-%d') 
df['days'] = df.apply(lambda x: max((min(x.enddate, period_end) - max(x.startdate, period_start)).days + 1, 0), axis=1) 

#percentage of days in range by person 
people_pct = df.groupby('person').apply(lambda x: x.days.sum()/((period_end - period_start).days + 1)) 
print(people_pct.head()) 

----------------- 
    person 
    A 0.196721 
    B 0.166667 

你在正確的軌道上 - 大熊貓groupby是偉大的細分數據,但真正的力量來自.apply()功能,它可以做一個共用的數學變換(mean,std等),或者在這種情況下是一個自定義函數。

該申請中的lambda表示對於組內的每個行/列(取決於axis),執行此自定義功能並返回Series「。

雖然這涵蓋了你的問題,但它仍然缺乏檢測獨特的日子,所以我們假設行被拆分爲不重疊,如你的例子所述。

+0

這是一個非常棒的清潔解決方案。 – user2242044

+0

謝謝!我建議你看看http://pandas.pydata.org/pandas-docs/stable/groupby.html。在清理數據時,這是一個偉大的思維模式 – dylanjf

+0

我意識到如果有重疊可以產生超過100%的百分比,那麼代碼將重複計算幾天。例如,如果數據是[['2017-01-01','2017-01-02','A'],['2017-01-01','2017-01-03','A' ]]'解決這個問題的任何想法? – user2242044