拆分Pandas DataFrame中的List

时间:2015-02-10 21:41:17

标签: python csv pandas

我有一个包含许多列的csv文件。使用pandas,我将这个csv文件读入一个数据帧,并有一个日期时间索引和五到六个其他列。

其中一列是时间戳列表(下面带索引的示例)

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')]

我想要做的是将时间戳列转换为列出的每个时间戳的单独行。例如,对于第1行,它将转换为4行,第2行将转换为1行。我意识到我需要重置索引才能做到这一点,这很好。

我尝试的所有东西最终都会进入左侧领域(获取值并在熊猫之外创建一个列表等)

任何建议表示赞赏。

5 个答案:

答案 0 :(得分:13)

如果你想留在纯大熊猫中,你可以投入一个棘手的groupbyapply,如果你不计算列重命名,最终会沸腾到一个班轮。< / p>

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索引是有用的。 / p>

我们希望日期成为新行的单个索引,因此我们使用groupby将所需的行值放入索引中。然后在该操作中我想仅拆分此日期的列表,这是apply将为我们做的。

我传递apply一个由单个列表组成的pandas Series,但我可以通过.values[0]访问该列表,该Series推送{{1}的唯一行带有单个条目的数组。

要将列表转换为一组将传递回索引日期的行,我可以将其设为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]))

此时一个衬管变得越来越密集,你应该投入一个功能。

答案 1 :(得分:7)

我这样做的方法是将列表拆分成单独的列,然后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

答案 2 :(得分:1)

这并不是非常pythonic,但它有效(假设你的createDate是唯一的!)

Apply只返回比groupby更多的行,所以我们将人工使用groupby(即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)

跟进:如果CreateDate不是唯一的,您可以将索引重置为新列并将其重新组合。

答案 3 :(得分:1)

从性能角度来看,可能不是最佳方式,但您仍然可以利用itertools包:

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

答案 4 :(得分:0)

一种更新的方法是使用 explode (documentation)

import pandas as pd

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')]]}

test_df = pd.DataFrame(d)

result_df = test_df.explode('ts')

result_df.head()

输出

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