python熊猫从一系列布尔值中获取索引边界
我正在尝试根据一些特征剪辑视频。 我目前的策略是为每一帧创建一系列布尔值,并按时间戳进行索引python熊猫从一系列布尔值中获取索引边界,python,pandas,Python,Pandas,我正在尝试根据一些特征剪辑视频。 我目前的策略是为每一帧创建一系列布尔值,并按时间戳进行索引True保存它,而False转储它 当我计划剪切视频时,我需要从这个列表中提取边界,这样我就可以告诉fmpeg我想从主视频中提取的部分的开始和结束 总结如下: 我有一个熊猫系列,看起来像这样: acquisitionTs 0.577331 False 0.611298 False 0.645255 False 0.679218 False 0.716538 Fals
True
保存它,而False
转储它
当我计划剪切视频时,我需要从这个列表中提取边界,这样我就可以告诉fmpeg我想从主视频中提取的部分的开始和结束
总结如下:
我有一个熊猫系列,看起来像这样:
acquisitionTs
0.577331 False
0.611298 False
0.645255 False
0.679218 False
0.716538 False
0.784453 True
0.784453 True
0.818417 True
0.852379 True
0.886336 True
0.920301 True
0.954259 False
...
83.393376 False
83.427345 False
dtype: bool
(由于显示原因而被截断,但时间戳通常从0开始)
我需要得到True
序列的边界,所以在这个例子中,我应该得到[[t_0,t_1],[t_2,t_3]n,[t_2n-1,t_2n]
,如果在我的系列中有t_0=0.784453
和t_1=0.920301
不同的True
现在这个问题看起来很简单,事实上,您可以将序列移位1,然后在之间进行异或运算,得到一个布尔值列表,其中True
表示边界
e = df.shift(periods=1, freq=None, axis=0)^df
print(e[e].index)
(带df
为熊猫系列)
还有一些工作要做,比如计算第一个元素是上升沿还是下降沿,但这种方法是有效的
然而,这似乎不是很像蟒蛇。事实上,问题很简单,我相信在pandas
、numpy
甚至python
中一定有一个预先构建的函数,它可以很好地适应单个函数调用,而不是像上面那样的hack。虽然groupby
函数似乎很有前途,但我以前从未使用过它
这样做的最佳方式是什么?您可以用它来识别True
s的集群:
In [102]: ts
Out[102]:
0.069347 False
0.131956 False
0.143948 False
0.224864 False
0.242640 True
0.372599 False
0.451989 False
0.462090 False
0.579956 True
0.588791 True
0.603638 False
0.625107 False
0.642565 False
0.708547 False
0.730239 False
0.741652 False
0.747126 True
0.783276 True
0.896705 True
0.942829 True
Name: keep, dtype: bool
In [103]: groups, nobs = ndimage.label(ts); groups
Out[103]: array([0, 0, 0, 0, 1, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3], dtype=int32)
拥有组
数组后,可以使用以下方法查找相关时间:
比如说,
import numpy as np
import pandas as pd
import scipy.ndimage as ndimage
np.random.seed(2016)
def make_ts(N, ngroups):
times = np.random.random(N)
times = np.sort(times)
idx = np.sort(np.random.randint(N, size=(ngroups,)))
arr = np.zeros(N)
arr[idx] = 1
arr = arr.cumsum()
arr = (arr % 2).astype(bool)
ts = pd.Series(arr, index=times, name='keep')
return ts
def find_groups(ts):
groups, nobs = ndimage.label(ts)
df = pd.DataFrame({'times': ts.index, 'group': groups})
result = (df.loc[df['group'] != 0]
.groupby('group')['times']
.agg({'start':'first','end':'last'}))
return result
ts = make_ts(20, 5)
result = find_groups(ts)
屈服
start end
group
1 0.242640 0.242640
2 0.579956 0.588791
3 0.747126 0.942829
要以列表的形式获取开始和结束时间,可以使用:
In [125]: result.values.tolist()
Out[125]:
[[0.24264034406127022, 0.24264034406127022],
[0.5799564094638113, 0.5887908182432907],
[0.7471260123697537, 0.9428288694956402]]
使用
ndimage.label
很方便,但请注意,也可以在不使用scipy的情况下计算:
def find_groups_without_scipy(ts):
df = pd.DataFrame({'times': ts.index, 'group': (ts.diff() == True).cumsum()})
result = (df.loc[df['group'] % 2 == 1]
.groupby('group')['times']
.agg({'start':'first','end':'last'}))
return result
这里的主要思想是使用(ts.diff()==True).cumsum()查找True
s集群的标签ts.diff()==True
给出的结果与ts.shift()^ts
的结果相同,但要快一点。取累积和(即调用cumsum
)将True
视为等于1,将False
视为等于0,因此每次遇到True
时,累积和将增加1。因此,每个集群都会使用不同的编号进行标记:
In [111]: (ts.diff() == True).cumsum()
Out[111]:
0.069347 0
0.131956 0
0.143948 0
0.224864 0
0.242640 1
0.372599 2
0.451989 2
0.462090 2
0.579956 3
0.588791 3
0.603638 4
0.625107 4
0.642565 4
0.708547 4
0.730239 4
0.741652 4
0.747126 5
0.783276 5
0.896705 5
0.942829 5
Name: keep, dtype: int64
我会使用一个数据帧而不是一个系列(它实际上也适用于一个系列)
我会:
df[df.Value.diff().fillna(False)]
acquisitionTs Value
5 0.784453 True
7 0.818417 False
8 0.852379 True
11 0.954259 False
因此,正如您知道的第一个值False一样,您知道0-4是False,然后它在每个索引(5,7,8,11)处切换
我认为,groupby
函数对您没有帮助,因为它会破坏真/假值的顺序(在我的示例中,您将有2组,而不是5组)。这些都是很好的解决方案,但我认为可能有一个更简单、更普遍适用的选项
在其核心,您要查找某个值是否与前一个值不同。如果您将其与自身进行比较,但移动了1,您将得到您想要的。您还可以获得快速比较操作的好处
将熊猫作为pd导入
#创建一个系列
系列1=pd.系列(['duck','duck','duck','duck','duck','goose','goose','duck']))
#创建系列的副本,移动1个空间
系列_2=系列_1.移位(1)
#比较原始系列和移位系列,以获得新的“它是边缘吗?”系列
is_edge=系列_1!=系列2
数据帧({'data':series_1,'edge':is_edge})
或者,如果比较栏中包含的内容更清楚:
让这个例子用数字索引和布尔值直接说明你的问题:
series_1=pd.series({.1:True、.2:True、.3:False、.4:False、.5:True、.6:True})
系列_2=系列_1.移位(1)
is_edge=系列_1!=系列2
数据帧({'original':series_1,'shift':series_2,'edge':is_edge})
很好地利用了您可以支配的资源,而不是引入额外的依赖项。谢谢您的回答!然而,对于第一个元素,您的代码似乎并不不可知,第一个元素可能是真的,也可能是假的,因此您将以与您首先想要的相反的结果结束。一个简单的解决方法是在结果中插入第一行,如果它是真的(最后一行也会发生同样的情况),感谢您的帮助!编辑:实际上,我们可以只观察结果的第一个(和最后一个)元素的值,它告诉我们边缘是上升还是下降,所以一开始并没有什么问题。
df
acquisitionTs Value
0 0.577331 False
1 0.611298 False
2 0.645255 False
3 0.679218 False
4 0.716538 False
5 0.784453 True
6 0.784453 True
7 0.818417 False
8 0.852379 True
9 0.886336 True
10 0.920301 True
11 0.954259 False
df[df.Value.diff().fillna(False)]
acquisitionTs Value
5 0.784453 True
7 0.818417 False
8 0.852379 True
11 0.954259 False