如何检查系列中的值是否包含在定义DataFrame行的任何时间间隔内?

时间:2013-04-07 16:25:53

标签: pandas vectorization

我意识到我的头衔有点令人困惑,但我想如果我们以身作则,我会更清楚。我想要做的是一个矢量化测试,以检查给定系列中的任何值是否包含在由startstop列的DataFrame对象定义的任何间隔中。

考虑系列valid,它是名为trials的DataFrame的列。以下是trials的样子:

<class 'pandas.core.frame.DataFrame'>
Int64Index: 156 entries, 0 to 155
Data columns (total 3 columns):
start    156  non-null values
stop     156  non-null values
valid    156  non-null values
dtypes: bool(1), float64(2)

我有一个名为'blink`的单独DataFrame。它有三列:

<class 'pandas.core.frame.DataFrame'>
Int64Index: 41 entries, 0 to 40
Data columns (total 3 columns):
tstart    41  non-null values
tstop     41  non-null values
dur       41  non-null values
dtypes: bool(1), float64(2)

最后一栏并不直接相关:它是眼球的持续时间,即tstoptstart之间的差异。

我想将trials['valid']的每一行设置为False,如果其对应的trials['start']trials['stop']之间的间隔与任何重叠blink['tstart']blink['tstop']间隔。{/ p>

可以遍历行并使用np.arangein运算符在嵌套循环中执行此操作,但实际上需要数小时(我的实际数据集)比这个虚拟例子大得多)。我可以使用矢量化方法吗?如果没有,是否有更快的基于迭代的方法?

如果有任何不清楚的地方,我当然乐意提供更多细节。

2 个答案:

答案 0 :(得分:1)

你的眨眼数据

In [27]: blink = pd.DataFrame(dict(tstart = [0,10], tstop = [5,15]))

In [28]: blink_s = blink.stack()

In [29]: blink_s.index = [ "%s_%s" % (v,i) for i, v in blink_s.index ]

构造一系列眨眼(有点像转动),但我们需要新的名字

In [37]: blink_s
Out[37]: 
tstart_0     0
tstop_0      5
tstart_1    10
tstop_1     15

试用数据

In [30]: trial = pd.DataFrame(dict(start = [3,7,12],stop=[4,10,16]))

在试验行中平铺blink_s

In [32]: blink_df = pd.DataFrame([ blink_s for i in trial.index ])

加入

In [33]: x = trial.join(blink_df)

In [34]: x
Out[34]: 
   start  stop  tstart_0  tstop_0  tstart_1  tstop_1
0      3     4         0        5        10       15
1      7    10         0        5        10       15
2     12    16         0        5        10       15

你的答案是一个矢量化布尔表达式(这可能是一个很长的表达式,所以你应该以编程方式生成它,但这样做并不复杂)

In [35]: x['valid'] = ((x.start>x.tstart_0) & (x.stop<=x.tstop_0)) | ((x.start>x.tstart_1)&(x.stop<=x.tstop_1))

In [36]: x
Out[36]: 
   start  stop  tstart_0  tstop_0  tstart_1  tstop_1  valid
0      3     4         0        5        10       15   True
1      7    10         0        5        10       15  False
2     12    16         0        5        10       15  False

如果您想将浮点数据作为tstart / tstop标准,这将有效。如果将间隔限制为仅仅int数据,则解决方案稍微简单一点,因为您可以只创建一系列包含的值(如blink_s),而只需执行{{1} }。

从本质上讲,您将闪烁框架展平为一系列,然后您可以将其应用于每个试用

使用Isin(和OP数据):

转换为int64数据

isin

添加一行我们知道的是范围

trial = pd.load('trials.pickle').reindex(columns=['start','stop']).astype('int64')
blink = pd.load('blink.pickle').astype('int64')

构造我们想要测试的值范围

trial = trial.append(pd.Series(dict(start=1000,stop=1200)),ignore_index=True)

如果传递的开始/结束位于选择范围

,则返回true
selections = []
for r in blink.iterrows():
    e = r[1]
    selections.extend(list(np.arange(e['tstart'],e['tstop'])))
selections = pd.Series(selections)

我插入了一行我知道会传递的行,没有其他行传递

def f(s):
    return s.isin(selections).all()
trial['valid'] = trial.apply(f,axis=1)

trial[trial.valid]

答案 1 :(得分:0)

假设trials的大小为mblink的大小为n

首先,在检查blink中的每一行之前,按tstarttrials进行排序,合并重叠的一行,需要O(n log n),看看{{3} }}

在检查启动/停止对是否有效时,请遵循以下算法。

  1. 使用二进制搜索在已排序的tstarts
  2. 中插入开头
  3. 使用二进制搜索在已排序的tstarts
  4. 中插入停靠点
  5. 如果开始和结束之间有tstart,则返回True
  6. 在开始之前找到一个,找到它的tstop,   如果它们与开始/停止对重叠,则返回True
  7. 返回错误
  8. 上述算法可能会帮助您降低时间复杂度,以检查start/stop对是否与blink中的任何对重叠,而不是O(n)O(log n)重叠,其中n是长度blink

    时间复杂度将从O(mn)降低到O(m log n) + O(n log n)。如果m >> log nn很大,那可能对您有很大帮助。