[英]How to summarize pandas dataframe
我有一個包含大約 20,xxx 條公交登機數據記錄的熊貓數據框。 數據集包含一個cardNumber
字段,該字段對於每個乘客都是唯一的。 有一個type
字段用於標識登機類型。 有一個routeName
列指定登機發生在哪條路線,最后一個Date
列標識登機發生的時間。 我在下面提供了一個模擬數據框。
df = pd.DataFrame(
{'cardNumber': ['999', '999', '999', '999', '901', '901', '888', '888'],
'type': ['trip_pass', 'transfer', 'trip_pass', 'transfer', 'stored_value', 'transfer', 'trip_pass',
'trip_pass'],
'routeName': ['1', '2', '2', '1', '20', '3', '4', '4'],
'Date': ['2020-08-01 06:18:56 -04:00', '2020-08-01 06:46:12 -04:00', '2020-08-01 17:13:51 -04:00',
'2020-08-01 17:47:32 -04:00', '2020-08-10 15:23:16 -04:00', '2020-08-10 15:44:45 -04:00',
'2020-08-31 06:54:09 -04:00', '2020-08-31 16:23:41 -04:00']}
)
df['Date'] = pd.to_datetime(df['Date'])
我想做的是總結轉移活動。 從 Route 1 到 Route 2 或從 Route 2 到 Route 1 平均發生的換乘次數。數據集中有 11 條不同的路線可以在它們之間進行換乘。
我希望輸出看起來像(請注意,下面的輸出不是從上面提供的示例中生成的):
From | To | Avg. Daily
----------------------------------
1 | 2 | 45.7
1 | 3 | 22.6
20 | 1 | 12.2
以下代碼適用於您提供的塊數據。 如果它在您的實際數據中不起作用,請告訴我。 可能有更好的方法來做到這一點,但我認為這是一個很好的起點。
這里的總體思路是按乘客分組來確定路線。 然后,由於您想要每日平均值,因此您需要按日期分組,然后按目的地分組以計算每日平均值。
# Define a function to get routes' relationship (origin vs destination)
def get_routes(x):
if 'transfer' not in x.type.tolist(): # if no 'transfer' type in group, leave it as 0 (we'll remove them afterwards)
return 0
x = x[x.type == 'transfer'] # select target type
date = df[df.cardNumber=='999'].Date.dt.strftime('%m/%d/%Y').unique()
if date.size == 1: # if there is more than one date by passenger, you'll need to change this code
date = date[0]
else:
raise Exception("There are more than one date per passenger, please adapt your code.")
s_from = x.routeName[x.Date.idxmin()] # get route from the first date
s_to = x.routeName[x.Date.idxmax()] # get route from the last date
return date, s_from, s_to
# Define a function to get the routes' daily average
def get_daily_avg(date_group):
daily_avg = (
date_group.groupby(['From', 'To'], as_index=False) # group the day by routes
.apply(lambda route: route.shape[0] / date_group.shape[0]) # divide the total of trips of that route by the total trips of that day
)
return daily_avg
# Get route's relationship
routes_series = df.groupby('cardNumber').apply(get_routes) # retrive routes per passenger
routes_series = routes_series[routes_series!=0] # remove groups without the target type
# Create a named dataframe from the series output
routes_df = pd.DataFrame(routes_series.tolist(), columns=['Date', 'From', 'To'])
# Create dataframe, perform filter and calculations
daily_routes_df = (
routes_df.query('From != To') # remove routes with same destination as the origin
.groupby('Date').apply(get_daily_avg) # calculate the mean per date
.rename(columns={None: 'Avg. Daily'}) # set name to previous output
.drop(['From','To'], axis = 1) # drop out redundant info since there's such info at the index
.reset_index() # remove MultiIndex to get a tidy dataframe
)
# Visualize results
print(daily_routes_df)
輸出:
Date From To Avg. Daily
0 08/01/2020 2 1 1.0
在這里,平均值為 1,因為每組只有一個計數。 請注意,只有“傳輸”類型已被考慮在內。 沒有它或沒有改變路線的那些被進一步刪除。
如果我的問題是正確的,您希望從您的事件中獲取旅行的開始和結束,並且第一個事件與起點(路線名稱)相對應,然后計算您在數據集中擁有的門票數量相同的起點和終點。
如果是這樣,您可以按如下方式執行此操作
# srot the dataframe so you can use first/last
df_sorted= df.sort_values(['cardNumber', 'Date']).reset_index(drop=True)
# calculate the counts do the counts, but only
# from the defined types
indexer_trip_points= df_sorted['type'].isin(['transfer'])
df_from_to= df_sorted[indexer_trip_points].groupby('cardNumber').agg(
start_date=('Date', 'first'),
trip_start=('routeName', 'first'),
trip_end=('routeName', 'last'),
)
df_from_to['start_date']= df_from_to['start_date'].dt.date
df_counts= df_from_to.groupby(['trip_start', 'trip_end', 'start_date']).agg(
count=('trip_start', 'count')
)
df_counts.reset_index(drop=False, inplace=True)
df_counts.groupby(['trip_start', 'trip_end']).agg(
avg=('count', 'mean')
)
這導致:
avg
trip_start trip_end
2 1 1
3 3 1
如您所見,最后一個條目的起始點與端點相同。 因此,您可能需要過濾掉您還沒有完整數據的行程。 例如,如果在您的情況下,一條路線永遠不會以與開始時相同的 routeName 結束,您可以通過比較兩列來簡單地過濾它們。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.