2017-01-23 149 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