2017-01-23 146 views
1

我寫了下面的代碼預處理的數據集是這樣的:優化Python代碼

StartLocation StartTime EndTime 
school   Mon Jul 25 19:04:30 GMT+01:00 2016 Mon Jul 25 19:04:33 GMT+01:00 2016 
...    ...   ... 

它包含與起始和結束時間的用戶參加了位置的列表。每個位置可能會發生多次,並且沒有全面的位置列表。由此,我想彙總每個位置的數據(頻率,總時間,平均時間)。爲此,我編寫了以下代碼:

def toEpoch(x): 
    try: 
     x = datetime.strptime(re.sub(r":(?=[^:]+$)", "", x), '%a %b %d %H:%M:%S %Z%z %Y').strftime('%s') 
    except: 
     x = datetime.strptime(x, '%a %b %d %H:%M:%S %Z %Y').strftime('%s') 
    x = (int(x)/60) 
    return x 

#Preprocess data 
df = pd.read_csv('...') 
for index, row in df.iterrows(): 
    df['StartTime'][index] = toEpoch(df['StartTime'][index]) 
    df['EndTime'][index] = toEpoch(df['EndTime'][index]) 
    df['TimeTaken'][index] = int(df['EndTime'][index]) - int(df['StartTime'][index]) 
total = df.groupby(df['StartLocation'].str.lower()).sum() 
av = df.groupby(df['StartLocation'].str.lower()).mean() 
count = df.groupby(df['StartLocation'].str.lower()).count() 
output = pd.DataFrame({"location": total.index, 'total': total['TimeTaken'], 'mean': av['TimeTaken'], 'count': count['TimeTaken']}) 
print(output) 

此代碼功能正常,但效率相當低。我如何優化代碼?

編輯:基於@Batman's有用的意見我不再迭代。但是,如果可能的話,我仍然希望進一步優化。更新的代碼是:

df = pd.read_csv('...') 
df['StartTime'] = df['StartTime'].apply(toEpoch) 
df['EndTime'] = df['EndTime'].apply(toEpoch) 
df['TimeTaken'] = df['EndTime'] - df['StartTime'] 
total = df.groupby(df['StartLocation'].str.lower()).sum() 
av = df.groupby(df['StartLocation'].str.lower()).mean() 
count = df.groupby(df['StartLocation'].str.lower()).count() 
output = pd.DataFrame({"location": total.index, 'total': total['TimeTaken'], 'mean': av['TimeTaken'], 'count': count['TimeTaken']}) 
print(output) 
+0

你應該只組一次,然後得到'sum','mean'和'count' – furas

+0

你真的需要'.str.lower()'嗎?你真的需要正則表達式嗎? – furas

+0

@furas手動輸入位置,這是必要的,正則表達式用於處理使用的異常時間戳。 (請參閱[this](https://stackoverflow.com/questions/41782874/valueerror-parsing-time-string)) – user7347576

回答

2

我會做的第一件事是停止遍歷行。

df['StartTime'] = df['StartTime'].apply(toEpoch) 
df['EndTime'] = df['EndTime'].apply(toEpoch) 
df['TimeTaken'] = df['EndTime'] - df['StartTime'] 

然後,做一個單獨的groupby操作。

gb = df.groupby('StartLocation') 
total = gb.sum() 
av = gb.mean() 
count = gb.count() 
+0

我是否也可以計算沒有迭代的時間? – user7347576

+0

@ user7347576 yes'df ['TimeTaken'] = df ['EndTime'] - df ['StartTime']'(如果EndTime和StartTime中有數字) – furas

+0

是的。我編輯了答案。 – Batman

2
  • 矢量化的日期轉換
  • 採取的兩個系列的時間戳的差異給出了一系列timedeltas的
  • 使用total_seconds從該timedeltas
  • groupby得到秒agg

# convert dates 
cols = ['StartTime', 'EndTime'] 
df[cols] = pd.to_datetime(df[cols].stack()).unstack() 

# generate timedelta then total_seconds via the `dt` accessor 
df['TimeTaken'] = (df.EndTime - df.StartTime).dt.total_seconds() 

# define the lower case version for cleanliness 
loc_lower = df.StartLocation.str.lower() 

# define `agg` functions for cleanliness 
# this tells `groupby` to use 3 functions, sum, mean, and count 
# it also tells what column names to use 
funcs = dict(Total='sum', Mean='mean', Count='count') 
df.groupby(loc_lower).TimeTaken.agg(funcs).reset_index() 

enter image description here


日期轉換的解釋

  • 我定義cols爲了方便
  • df[cols] =是賦值這兩列
  • pd.to_datetime()是一個矢量日期轉換器,但只需要pd.Seriespd.DataFrame
  • df[cols].stack()使2列的數據幀成一個系列,現在已經準備好爲pd.to_datetime()
  • 使用pd.to_datetime(df[cols].stack())所描述和unstack()找回我的2列,現在準備被分配。
+0

你能解釋一下這是什麼嗎? – user7347576

+0

@ user7347576解釋:-) – piRSquared

+0

@piRSqaured我不是故意浪費你的時間,但我仍然不明白爲什麼這會更快,我會在哪裏使用它? – user7347576