2017-01-23 3 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) 

Этот код функционирует правильно, однако довольно неэффективен. Как я могу оптимизировать код?

EDIT: Основано на @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.Series не pd.DataFrame
  • df[cols].stack() делает 2 колонков dataframe в ряд, теперь готов к pd.to_datetime()
  • использования pd.to_datetime(df[cols].stack()), как описан и unstack() получить обратно мою 2-колонку и теперь готовы быть назначенным.
+0

Не могли бы вы объяснить, что это делает? – user7347576

+0

@ user7347576 объяснил :-) – piRSquared

+0

@piRSqaured Я не хочу тратить ваше время, но я до сих пор не понимаю, почему это было бы быстрее и где я буду использовать его? – user7347576

Смежные вопросы