pandas DataFrame

时间:2017-06-29 17:04:53

标签: python pandas

给定一个看起来像这样的DataFrame

Index   Time               Val 
1       2017-06-29 17:48    0
2       2017-06-29 17:49    0
3       2017-06-29 17:50    1
4       2017-06-29 17:51    2
5       2017-06-29 17:52    3
6       2017-06-29 17:53    0
7       2017-06-29 17:54    0
8       2017-06-29 17:55    0
9       2017-06-29 17:56    0
10      2017-06-29 17:57    0

如何将其拆分为两个列表的数据框,其中一个列表是val == 0的块,一个是val > 0val < 0所在的块没有发生)。但是,存在以下复杂情况。当val > 0时,下一次val == 0应该开始新的数据框,反之亦然。

因此,基于上面的数据框,第一个列表将包含两个数据帧:一个包含索引1-2,另一个包含索引6-9。第二个列表应包含一个索引3-5的数据帧。

我知道我可以通过执行val > 0删除df[df.val == 0]行,这将给出下面的数据框,但由于索引2和6之间的中断,我需要它们在不同的数据帧中。

Index   Time               Val 
1       2017-06-29 17:48    0
2       2017-06-29 17:49    0
6       2017-06-29 17:53    0
7       2017-06-29 17:54    0
8       2017-06-29 17:55    0
9       2017-06-29 17:56    0
10      2017-06-29 17:57    0

N.B。这需要扩展到大数据帧(数百万行),因此需要速度。遍历每一行并寻找索引(或时间戳)的中断是不可取的。

1 个答案:

答案 0 :(得分:1)

我无法保证以下内容会快速运行,但它应该会让你走得很远。这背后的想法是在run-length encoding列上使用所谓的Val生成一个新列,通过该列对数据帧进行分组。以下应该是一个不错的开始:

import pandas as pd
from pandas import Timestamp
from itertools import groupby
from functools import reduce


d = {'Time': [Timestamp('2017-06-29 17:48:00'),
  Timestamp('2017-06-29 17:49:00'),
  Timestamp('2017-06-29 17:50:00'),
  Timestamp('2017-06-29 17:51:00'),
  Timestamp('2017-06-29 17:52:00'),
  Timestamp('2017-06-29 17:53:00'),
  Timestamp('2017-06-29 17:54:00'),
  Timestamp('2017-06-29 17:55:00'),
  Timestamp('2017-06-29 17:56:00'),
  Timestamp('2017-06-29 17:57:00')],
 'Val': [0, 0, 1, 2, 3, 0, 0, 0, 0, 0]}

df = pd.DataFrame(d)

df['grouper'] = reduce(list.__add__, ([x]*len(list(y[1])) for x, y in enumerate(groupby(df.Val, key=lambda x: x > 0))))

bins = [[], []]
for _, frame in df.groupby('grouper'):
    if (frame.Val == 0).all():
        bins[0].append(frame.iloc[:, :-1])
    else:
        bins[1].append(frame.iloc[:, :-1])


print(bins)

应该产生以下列表:

# [[                 Time  Val
# 0 2017-06-29 17:48:00    0
# 1 2017-06-29 17:49:00    0,                  Time  Val
# 5 2017-06-29 17:53:00    0
# 6 2017-06-29 17:54:00    0
# 7 2017-06-29 17:55:00    0
# 8 2017-06-29 17:56:00    0
# 9 2017-06-29 17:57:00    0], [                 Time  Val
# 2 2017-06-29 17:50:00    1
# 3 2017-06-29 17:51:00    2
# 4 2017-06-29 17:52:00    3]]

这里的想法是您在run-length encoding列上应用Val,这基本上意味着 您计算相等值的运行长度 。此过程的输出保存在名为grouper的新列中。此列用于对初始数据帧进行分组。分组完成后,您可以继续将bins列表中的单独数据框放在for-loop上。

虽然我无法保证速度,但我相信这个想法可以非常轻松地为您提供所需的输出。您可以尝试使用run-length encoding实现numpy想法以获得一点速度。

编辑:

如上所述,当groupby(df.Val, key=lambda x: x > 0)包含空值时调用df.Val,则不满足条件,因为NaN > 0应返回False。在这种情况下,分组变得有缺陷,导致意外的输出。由于目标是区分等于0的值和不等于key的值,因此您可以在使用groupby(df.Val, key=lambda x: x == 0)进行分组时更改传递给==参数的函数。以下几乎与上述内容相同,唯一的例外是>而不是d = {'Time': [Timestamp('2017-06-29 17:48:00'), Timestamp('2017-06-29 17:49:00'), Timestamp('2017-06-29 17:50:00'), Timestamp('2017-06-29 17:51:00'), Timestamp('2017-06-29 17:52:00'), Timestamp('2017-06-29 17:53:00'), Timestamp('2017-06-29 17:54:00'), Timestamp('2017-06-29 17:55:00'), Timestamp('2017-06-29 17:56:00'), Timestamp('2017-06-29 17:57:00'), Timestamp('2017-06-29 17:58:00'), Timestamp('2017-06-29 17:59:00')], 'Val': [0, 0, 1, 2, 3, 0, None, 0, 0, 0, 0, None]} df = pd.DataFrame(d) df['grouper'] = reduce(list.__add__, ([x]*len(list(y[1])) for x, y in enumerate(groupby(df.Val, key=lambda x: x == 0)))) bins = [[], []] for _, frame in df.groupby('grouper'): if (frame.Val == 0).all(): bins[0].append(frame.iloc[:, :-1]) else: bins[1].append(frame.iloc[:, :-1]) # [[ Time Val # 0 2017-06-29 17:48:00 0.0 # 1 2017-06-29 17:49:00 0.0, Time Val # 5 2017-06-29 17:53:00 0.0, Time Val # 7 2017-06-29 17:55:00 0.0 # 8 2017-06-29 17:56:00 0.0 # 9 2017-06-29 17:57:00 0.0 # 10 2017-06-29 17:58:00 0.0], [ Time Val # 2 2017-06-29 17:50:00 1.0 # 3 2017-06-29 17:51:00 2.0 # 4 2017-06-29 17:52:00 3.0, Time Val # 6 2017-06-29 17:54:00 NaN, Time Val # 11 2017-06-29 17:59:00 NaN]]

@ViewChild('myForm') myForm;

我希望这会有所帮助。