import pandas as pd
data = [
[105, 0.662932],
[105, 0.662932],
[107, 0.052653], # sign changes between here
[108, -0.228060], # and here; first row has `value` closer to 0
[110, -0.740819],
[112, -1.188906],
[142, -0.228060], # sign changes between here
[143, 0.052654], # and here; second row has `value` closer to 0
[144, 0.349638],
]
df = pd.DataFrame(data, columns=["A", "value"])
# where the sign is the same between two elements, the diff is 0
# otherwise, it's either 2 or -2 (doesn't matter which for this use case)
# use periods=1 and =-1 to do a diff forwards and backwards
sign = df.value.map(np.sign)
diff1 = sign.diff(periods=1).fillna(0)
diff2 = sign.diff(periods=-1).fillna(0)
# now we have the locations where sign changes occur. We just need to extract
# the `value` values at those locations to determine which of the two possibilities
# to choose for each sign change (whichever has `value` closer to 0)
df1 = df.loc[diff1[diff1 != 0].index]
df2 = df.loc[diff2[diff2 != 0].index]
idx = np.where(abs(df1.value.values) < abs(df2.value.values), df1.index.values, df2.index.values)
df.loc[idx]
A value
2 107 0.052653
7 143 0.052654
感谢@Vince W. 提到应该使用np.where;我最初采用的是一种更复杂的方法。
编辑 - 请参阅下面的@useruser3483203 的答案,它比这快很多。通过在 numpy 数组而不是 pandas Series 上执行前几个操作(diff、abs、比较相等性),您甚至可以提高一点(当我重新运行他们的时间时快 2 倍)。不过,numpy 的 diff 与 pandas 中的不同,因为它删除了第一个元素,而不是为它返回 NaN。这意味着我们取回符号变化的第一行的索引,而不是第二行,并且需要添加一个才能获得下一行。
def find_min_sign_changes(df):
vals = df.value.values
abs_sign_diff = np.abs(np.diff(np.sign(vals)))
# idx of first row where the change is
change_idx = np.flatnonzero(abs_sign_diff == 2)
# +1 to get idx of second rows in the sign change too
change_idx = np.stack((change_idx, change_idx + 1), axis=1)
# now we have the locations where sign changes occur. We just need to extract
# the `value` values at those locations to determine which of the two possibilities
# to choose for each sign change (whichever has `value` closer to 0)
min_idx = np.abs(vals[change_idx]).argmin(1)
return df.iloc[change_idx[range(len(change_idx)), min_idx]]