在Pandas DataFrame中界定超过某个阈值的连续区域
我有一个Pandas数据框,里面有一些索引和介于0到1之间的数值,像这样:
6 0.047033
7 0.047650
8 0.054067
9 0.064767
10 0.073183
11 0.077950
我想要找出那些连续超过5个值都高于某个阈值(比如0.5)的区域的起始和结束点的元组。这样我就能得到类似下面的结果:
[(150, 185), (632, 680), (1500,1870)]
第一个元组表示一个区域,它从索引150开始,有35个值连续都高于0.5,并且在索引185结束(不包括185)。
我开始时只筛选出高于0.5的值,像这样:
df = df[df['values'] >= 0.5]
现在我得到了这样的值:
632 0.545700
633 0.574983
634 0.572083
635 0.595500
636 0.632033
637 0.657617
638 0.643300
639 0.646283
我不能展示我的实际数据集,但下面这个数据集应该能很好地代表我的情况:
import numpy as np
from pandas import *
np.random.seed(seed=901212)
df = DataFrame(range(1,501), columns=['indices'])
df['values'] = np.random.rand(500)*.5 + .35
结果是:
1 0.491233
2 0.538596
3 0.516740
4 0.381134
5 0.670157
6 0.846366
7 0.495554
8 0.436044
9 0.695597
10 0.826591
...
其中区域(2,4)有两个值高于0.5。不过这个区域太短了。 另一方面,区域(25,44)有19个值连续高于0.5,所以这个区域会被加入到列表中。
2 个回答
1
我觉得这个代码能打印出你想要的结果。它主要是参考了Joe Kington在这里的回答,我觉得给他点赞是合适的。
import numpy as np
# from Joe Kington's answer here https://stackoverflow.com/a/4495197/3751373
# with minor edits
def contiguous_regions(condition):
"""Finds contiguous True regions of the boolean array "condition". Returns
a 2D array where the first column is the start index of the region and the
second column is the end index."""
# Find the indicies of changes in "condition"
d = np.diff(condition,n=1, axis=0)
idx, _ = d.nonzero()
# We need to start things after the change in "condition". Therefore,
# we'll shift the index by 1 to the right. -JK
# LB this copy to increment is horrible but I get
# ValueError: output array is read-only without it
mutable_idx = np.array(idx)
mutable_idx += 1
idx = mutable_idx
if condition[0]:
# If the start of condition is True prepend a 0
idx = np.r_[0, idx]
if condition[-1]:
# If the end of condition is True, append the length of the array
idx = np.r_[idx, condition.size] # Edit
# Reshape the result into two columns
idx.shape = (-1,2)
return idx
def main():
import pandas as pd
RUN_LENGTH_THRESHOLD = 5
VALUE_THRESHOLD = 0.5
np.random.seed(seed=901212)
data = np.random.rand(500)*.5 + .35
df = pd.DataFrame(data=data,columns=['values'])
match_bools = df.values > VALUE_THRESHOLD
print('with boolian array')
for start, stop in contiguous_regions(match_bools):
if (stop - start > RUN_LENGTH_THRESHOLD):
print (start, stop)
if __name__ == '__main__':
main()
如果有更优雅的方法我会感到很惊讶。
24
你可以通过查看一系列数据和向右移动一行的数据,来找到每个连续区域的第一个和最后一个元素。然后再筛选出那些相隔足够远的元素对。
# tag rows based on the threshold
df['tag'] = df['values'] > .5
# first row is a True preceded by a False
fst = df.index[df['tag'] & ~ df['tag'].shift(1).fillna(False)]
# last row is a True followed by a False
lst = df.index[df['tag'] & ~ df['tag'].shift(-1).fillna(False)]
# filter those which are adequately apart
pr = [(i, j) for i, j in zip(fst, lst) if j > i + 4]
举个例子,第一个区域会是:
>>> i, j = pr[0]
>>> df.loc[i:j]
indices values tag
15 16 0.639992 True
16 17 0.593427 True
17 18 0.810888 True
18 19 0.596243 True
19 20 0.812684 True
20 21 0.617945 True