这是一个有趣的谜题。我找到了一种使用 pandas 工具的方法,但我想你会同意它更加不透明:-)。示例如下:
data = pandas.Series([1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1])
x = data[::-1] # reverse the data
print(x.groupby(((x<0) != (x<0).shift()).cumsum()).apply(lambda x: pandas.Series(
np.arange(len(x))+1 if (x<0).all() else np.zeros(len(x)),
index=x.index))[::-1])
输出正确:
0 0
1 3
2 2
3 1
4 0
5 2
6 1
7 0
8 0
9 1
10 0
dtype: float64
基本思想与我在对this question 的回答中描述的类似,您可以在各种询问如何利用 pandas 中的行间信息的答案中找到相同的方法。你的问题有点棘手,因为你的标准是相反的(要求 following 否定的数量而不是 preceding 否定的数量),并且因为你只想要一侧分组(即,您只需要连续负数的数量,而不是具有相同符号的连续数字的数量)。
这是相同代码的更详细版本,并带有一些解释,可能更容易掌握:
def getNegativeCounts(x):
# This function takes as input a sequence of numbers, all the same sign.
# If they're negative, it returns an increasing count of how many there are.
# If they're positive, it just returns the same number of zeros.
# [-1, -2, -3] -> [1, 2, 3]
# [1, 2, 3] -> [0, 0, 0]
if (x<0).all():
return pandas.Series(np.arange(len(x))+1, index=x.index)
else:
return pandas.Series(np.zeros(len(x)), index=x.index)
# we have to reverse the data because cumsum only works in the forward direction
x = data[::-1]
# compute for each number whether it has the same sign as the previous one
sameSignAsPrevious = (x<0) != (x<0).shift()
# cumsum this to get an "ID" for each block of consecutive same-sign numbers
sameSignBlocks = sameSignAsPrevious.cumsum()
# group on these block IDs
g = x.groupby(sameSignBlocks)
# for each block, apply getNegativeCounts
# this will either give us the running total of negatives in the block,
# or a stretch of zeros if the block was positive
# the [::-1] at the end reverses the result
# (to compensate for our reversing the data initially)
g.apply(getNegativeCounts)[::-1]
如您所见,运行长度式的操作在 pandas 中通常并不简单。但是,an open issue 可以添加更多的分组/分区功能来改善其中的一些问题。无论如何,您的特定用例有一些特定的怪癖,使其与典型的运行长度任务有点不同。