Разбиение списка внутри Pandas DataFrame

У меня есть файл csv, который содержит несколько столбцов. Используя pandas, я прочитал этот файл csv в фреймворке данных и имел индекс datetime и пять или шесть других столбцов.

Один из столбцов - это список временных меток (пример ниже с индексом)

CreateDate     TimeStamps
4/1/11         [Timestamp('2012-02-29 00:00:00'), Timestamp('2012-03-31 00:00:00'), Timestamp('2012-04-25 00:00:00'), Timestamp('2012-06-30 00:00:00')]
4/2/11         [Timestamp('2014-01-31 00:00:00')]
6/8/11         [Timestamp('2012-08-31 00:00:00'), Timestamp('2012-09-30 00:00:00'), Timestamp('2012-11-07 00:00:00'), Timestamp('2013-01-10 00:00:00'), Timestamp('2013-07-25 00:00:00')]

То, что я хотел бы сделать, - это преобразовать столбец timestamp в отдельные строки для каждой отметки времени. Например, для строки 1 она преобразуется в 4 строки, а строка 2 преобразуется в 1 строку. Я понимаю, что мне понадобится reset индекс, чтобы это сделать, и это нормально.

Все, что я пробовал, просто попадает в левое поле (беря значения и создавая список вне pandas и т.д.)

Любые предложения оценены.

Ответ 1

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

In [1]: import pandas as pd

In [2]: d = {'date': ['4/1/11', '4/2/11'], 'ts': [[pd.Timestamp('2012-02-29 00:00:00'), pd.Timestamp('2012-03-31 00:00:00'), pd.Timestamp('2012-04-25 00:00:00'), pd.Timestamp('2012-06-30 00:00:00')], [pd.Timestamp('2014-01-31 00:00:00')]]}

In [3]: df = pd.DataFrame(d)

In [4]: df.head()
Out[4]: 
     date                                                 ts
0  4/1/11  [2012-02-29 00:00:00, 2012-03-31 00:00:00, 201...
1  4/2/11                              [2014-01-31 00:00:00]

In [5]: df_new = df.groupby('date').ts.apply(lambda x: pd.DataFrame(x.values[0])).reset_index().drop('level_1', axis = 1)

In [6]: df_new.columns = ['date','ts']

In [7]: df_new.head()
Out[7]: 
     date         ts
0  4/1/11 2012-02-29
1  4/1/11 2012-03-31
2  4/1/11 2012-04-25
3  4/1/11 2012-06-30
4  4/2/11 2014-01-31

Поскольку цель состоит в том, чтобы принять значение столбца (в этом случае date) и повторить его для всех значений нескольких строк, которые вы намереваетесь создать из списка, полезно придумать индексацию pandas.

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

Я прохожу apply a pandas Series, который состоит из одного списка, но я могу получить доступ к этому списку через .values[0], который подталкивает единственную строку Series к массиву с одним запись.

Чтобы превратить список в набор строк, который будет передан обратно в индексированную дату, я могу просто сделать его DataFrame. Это приводит к штрафу за получение дополнительного индекса, но в итоге мы его отбрасываем. Мы могли бы сделать это самим индексом, но это исключало бы значения обмана.

Как только это будет возвращено, у меня есть мультииндекс, но я могу заставить это в формате строки, который мы желаем, reset_index. Затем мы просто удаляем нежелательный индекс.

Это звучит, но на самом деле мы просто используем естественное поведение функций pandas, чтобы избежать явного итерации или цикла.

Скорость мудрый, как правило, очень хорош, и поскольку он полагается на apply, здесь могут работать любые трианки распараллеливания, которые работают с apply.

Необязательно, если вы хотите, чтобы он был надежным для нескольких дат, каждый со вложенным списком:

df_new = df.groupby('date').ts.apply(lambda x: pd.DataFrame([item for sublist in x.values for item in sublist]))

в этот момент один вкладыш становится плотным, и вы должны, вероятно, использовать функцию.

Ответ 2

Как я это сделал, был разбит список на отдельные столбцы, а затем melt отредактировал его, чтобы поместить каждую метку времени в отдельную строку.

In [48]: df = pd.DataFrame([[1,2,[1,2,4]],[4,5,[1,3]],],columns=['a','b','TimeStamp'])
    ...: df
Out[48]: 
   a  b  TimeStamp
0  1  2  [1, 2, 4]
1  4  5     [1, 3]

Вы можете преобразовать столбец в список, а затем вернуться к DataFrame, чтобы разбить его на столбцы:

In [53]: TScolumns = pd.DataFrame(df.TimeStamp.tolist(), )
    ...: TScolumns
Out[53]: 
   0  1   2
0  1  2   4
1  1  3 NaN

И затем соедините его с исходным фреймворком данных

In [90]: df = df.drop('TimeStamp',axis=1)
In [58]: split = pd.concat([df, TScolumns], axis=1)
    ...: split
Out[58]: 
   a  b  0  1   2
0  1  2  1  2   4
1  4  5  1  3 NaN

Наконец, используйте melt, чтобы получить нужную форму:

In [89]: pd.melt(split, id_vars=['a', 'b'], value_name='TimeStamp')
Out[89]: 
   a  b variable  TimeStamp
0  1  2        0          1
1  4  5        0          1
2  1  2        1          2
3  4  5        1          3
4  1  2        2          4
5  4  5        2        NaN

Ответ 3

Это не очень pythonic, но он работает (если ваш createDate уникален!)

Применить будет возвращать только больше строк, чем получается с помощью groupby, поэтому мы будем искусственно использовать группу (т.е. группировать по столбцу с уникальными значениями, поэтому каждая группа является одной строкой).

def splitRows(x):

    # Extract the actual list of time-stamps. 
    theList = x.TimeStamps.iloc[0]

    # Each row will be a dictionary in this list.
    listOfNewRows = list()

    # Iterate over items in list of timestamps, 
    # putting each one in a dictionary to later convert to a row, 
    # then adding the dictionary to a list. 

    for i in theList:
        newRow = dict()
        newRow['CreateDate'] = x.CreateDate.iloc[0]
        newRow['TimeStamps'] = i
        listOfNewRows.append(newRow)

    # Now convert these dictionaries into rows in a new dataframe and return it. 
    return pd.DataFrame(listOfNewRows)


df.groupby('CreateDate', as_index = False, group_keys = False).apply(splitRows)

Followup: если CreateDate НЕ уникален, вы можете просто reset индексировать новый столбец и группу.

Ответ 4

Вероятно, это не лучший способ с точки зрения производительности, но все же вы можете использовать itertools package:

from pandas import DataFrame, Timestamp
import itertools

d = {'date': ['4/1/11', '4/2/11'], 'ts': [[Timestamp('2012-02-29 00:00:00'), Timestamp('2012-03-31 00:00:00'), Timestamp('2012-04-25 00:00:00'), Timestamp('2012-06-30 00:00:00')], [Timestamp('2014-01-31 00:00:00')]]}
df = DataFrame(d)

res = df.to_dict()
data = []
for x in res['date'].keys():
  data.append(itertools.izip_longest([res['date'][x]], res['ts'][x], fillvalue=res['date'][x]))

new_data = list(itertools.chain.from_iterable(data))
df2 = DataFrame(new_data, columns=['date', 'timestamp'])
print df2

Будет напечатан:

     date  timestamp
0  4/1/11 2012-02-29
1  4/1/11 2012-03-31
2  4/1/11 2012-04-25
3  4/1/11 2012-06-30
4  4/2/11 2014-01-31