我有一个包含许多列的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行。我意识到我需要重置索引才能做到这一点,这很好。
我尝试的所有东西最终都会进入左侧领域(获取值并在熊猫之外创建一个列表等)
任何建议表示赞赏。
答案 0 :(得分:13)
如果你想留在纯大熊猫中,你可以投入一个棘手的groupby
和apply
,如果你不计算列重命名,最终会沸腾到一个班轮。< / 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