【问题标题】:Optimise Python Code优化 Python 代码
【发布时间】:2017-01-23 10:15:06
【问题描述】:

我编写了以下代码来预处理这样的数据集:

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 有用的 cmets 我不再迭代。但是,如果可能的话,我仍然希望进一步优化这一点。更新后的代码是:

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)

【问题讨论】:

  • 你应该只分组一次,然后得到summeancount
  • 你真的需要.str.lower() 吗?你真的需要正则表达式吗?
  • @furas 位置是手动输入的,所以它是必要的,正则表达式是用来处理不寻常的时间戳。 (见this
  • 使用 apply 仍在迭代中。

标签: python datetime pandas optimization dataframe


【解决方案1】:

我要做的第一件事是停止遍历行。

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()

【讨论】:

  • 我是否也能够计算所用时间而无需迭代?
  • @user7347576 是 df['TimeTaken'] = df['EndTime'] - df['StartTime'](如果您在 EndTimeStartTime 中有号码)
  • @Batman 在有效分组之前,我还可以将所有文本降低为小写吗?
  • 当然。使用df['StartLocation'].apply(str.lower)
  • @Batman 我不知道为什么,但较低的似乎并不总是有效。在我的最终输出中,它会生成“客厅”、“客厅”和“客厅”。任何想法为什么?
【解决方案2】:
  • 矢量化日期转换
  • 取两个系列时间戳的差值给出一系列时间增量
  • 使用 total_seconds 从 timedeltas 中获取秒数
  • groupbyagg

# 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()


日期转换说明

  • 为方便起见,我定义了cols
  • df[cols] = 是对这两列的赋值
  • pd.to_datetime() 是一个矢量化日期转换器,但只接受 pd.Series 而不是 pd.DataFrame
  • df[cols].stack() 将 2 列数据框变成一个系列,现在可以使用 pd.to_datetime()
  • 使用pd.to_datetime(df[cols].stack())unstack() 来取回我的2 列,现在可以分配了。

【讨论】:

  • 你能解释一下这是做什么的吗?
  • @user7347576 解释 :-)
  • @piRSqaured 我不是要浪费你的时间,但我还是不明白为什么这样做会更快以及我会在哪里使用它?
  • @user7347576 不用担心。我遗漏了细节,因为我要去善意捐赠东西。我以为你会飞跃,看看该怎么做。这是我的错。我会在一个小时左右告诉你该怎么做
  • @user7347576 你去。如果您有任何其他问题,请告诉我。
猜你喜欢
  • 2017-03-13
  • 2017-09-03
  • 2017-11-02
  • 2014-06-24
  • 2011-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多