2017-01-16 4 views
0

У меня есть CSV файл, как это:Панды: проблема с уникальной GroupBy и DateTime

id,timestamp 
1,2015-03-02 
2,2015-03-03 

который я затем загрузить в DataFrame, как это:

df = pd.read_csv('file.csv', index_col=['id'], parse_dates=['timestamp']) 

я группа по идентификатору, выберите столбец временной метки, и применить функцию для возврата метки времени - день

df.groupby(level='id')['timestamp'].apply(lambda x: x - pd.Timedelta('1 days')) 

результата:

id 
1 2015-03-01 
2 2015-03-02 
Name: timestamp, dtype: datetime64[ns] 

Однако, когда я применяю уникальный объект() к объекту groupby, временные метки изменяются в неожиданный формат.

df.groupby(level='id')['timestamp'].unique().apply(lambda x: x - pd.Timedelta('1 days')) 

id 
1 [2015-03-02T00:00:00.000000000] 
2 [2015-03-03T00:00:00.000000000] 
Name: timestamp, dtype: object 

Как сохранить формат дат?

+0

Проблема в том, что 'unique' будет возвращать массив, содержащий все уникальные значения. Чего вы пытаетесь достичь? Это похоже на проблему [XY] (http://meta.stackexchange.com/a/66378). – root

+0

@root tl; dr Я по существу пытаюсь объединить два DataFrames вместе, df1 и df2. df1 имеет столбец временной метки, а df2 имеет временную метку. Однако в df1 каждый индекс имеет несколько экземпляров, тогда как в df2 каждый индекс имеет только один экземпляр. Поэтому после присоединения я остаюсь с DataFrame, где каждый индекс имеет повторяющиеся значения столбца timestamp в df2. Вот почему я принимаю unique(), потому что мне нужны только уникальные временные метки. Я группирую идентификатор, потому что мне нужны все временные метки для каждого идентификатора. Затем для каждой отметки времени я применяю эту функцию. Но я смущен, почему формат даты и времени меняется. – cosmosa

+1

Можете ли вы разместить воспроизводимый набор данных (с дубликатами) и нужный набор данных? – MaxU

ответ

2

unique возвращает последовательность уникальных значений. Вот почему результат

df.groupby(level='id')['timestamp'].unique() 

- это серия списков.

Вместо того, чтобы удалить дубликаты, использовать drop_duplicates:

result = df.reset_index().drop_duplicates(subset=['id', 'timestamp']).set_index('id') 

Поскольку drop_duplicates требует подмножества, чтобы быть список столбцов, reset_index был использован выше, чтобы переместить уровень id индекса на колонку, и set_index был использован чтобы вернуть его обратно в индекс после удаления дубликатов.


Избегайте использования apply, если это возможно. При передаче пользовательской функции Python apply вызывает функцию в простой петле Python - таким образом, она медленнее, чем векторизованные операции. Если вы можете выполнить вычисление с помощью векторизованных операций, ваш код будет работать быстрее.

В этом случае, вероятно, будет быстрее вычесть 1 день из всей колонны все сразу (независимо от группы или дубликатов):

df['timestamp'] -= pd.Timedelta(days=1) 

один случай, когда это может быть медленнее, если DataFrame огромен, но состоит из только одной (или нескольких) групп ('id', 'timestamp'). Но в целом применение вексеризованной операции ко всему столбцу будет быстрее, чем несколько вызовов , применяемых к меньшим группам.


Так, например,

import pandas as pd 
import numpy as np 
Timestamp = pd.Timestamp 

df = pd.DataFrame({'timestamp': [Timestamp('2015-03-02 00:00:00'), Timestamp('2015-03-02 00:00:00'), Timestamp('2015-03-03 00:00:00'), Timestamp('2015-03-03 00:00:00')]}, index=pd.Index([1, 1, 1, 2], name='id'),) 
#  timestamp 
# id   
# 1 2015-03-02 
# 1 2015-03-02 
# 1 2015-03-03 
# 2 2015-03-03 

df['timestamp'] -= pd.Timedelta(days=1) 
result = df.reset_index().drop_duplicates(subset=['id', 'timestamp']).set_index('id') 

print(result) 
#  timestamp 
# id   
# 1 2015-03-01 
# 1 2015-03-02 
# 2 2015-03-02 
0

groupby возвращает массивы, поэтому вы видите, что они отображаются так. Если вы хотите серию, у которой есть отметки времени, вам нужно извлечь эти значения с помощью метода apply ниже.

grouped = df.groupby(level='id')['timestamp'].unique().apply(lambda x: x - pd.Timedelta('1 days')) 

grouped.apply(lambda x: x[0]) 

>

id 
1 2015-03-01 
2 2015-03-02 
Name: timestamp, dtype: datetime64[ns] 
0

Формат меняется в списке, потому что вы просите уникальных значений (из которых может быть несколько). Можно, например, вернуть только первый:

df.groupby(level='id')['timestamp'].unique().apply(lambda x: x[0] - pd.Timedelta('1 days'))

PS. Я предполагаю, что решение @ unutbu будет лучше для вас.

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