【问题标题】:How to find first occurrence of a significant difference in values of a pandas dataframe?如何找到熊猫数据框值的第一次显着差异?
【发布时间】:2020-07-20 05:54:58
【问题描述】:

Pandas DataFrame 中,如何找到两个相邻索引处的两个值之间第一次出现较大差异的地方?

例如,如果我有一个 DataFrameA 包含数据 [1, 1.1, 1.2, 1.3, 1.4, 1.5, 7, 7.1, 7.2, 15, 15.1],我希望索引保持 1.5,即 5。在下面的代码中,它会给我保持 7.2 的索引,因为15 - 7.2 > 7 - 1.5

idx = df['A'].diff().idxmax() - 1

我应该如何解决这个问题,以便获得第一个“大差异”出现的索引?

【问题讨论】:

标签: python pandas numpy dataframe


【解决方案1】:

主要问题当然是您如何定义“巨大差异”。您的解决方案很好地获得了最大的差异,仅通过使用 .diff(-1) 和使用 Jezrael 所示的绝对值来改进:

differences = df['A'].diff(-1).abs()

如果您的值未排序,则使用绝对值很重要,在这种情况下,您可能会得到负差异。

然后,您可能应该对这些值进行一些聚类,并获得具有最大值的集群的最小索引。 Jezrael 已经通过使用最大的四分位数展示了启发式方法,但是仅稍微修改您的示例就行不通了:

df = pd.DataFrame({'A': [1, 1.05, 1.2, 1.3, 1.4, 1.5, 7, 7.1, 7.2, 15, 15.1]})
differences = df['A'].diff(-1).abs()
idx = differences.index[differences >= differences.quantile(.75)][0]
print(idx, differences[idx])

这会返回1 0.1499999999999999

这里还有 3 个可能更适合您的启发式方法:

  • 如果您有一个高于该值的值,您认为差异“很大”(例如1.5):

    idx = differences.index[differences >= 1.5][0]
    
  • 如果你知道有多少个大值,你可以选择那些并得到最小的索引(例如2):

    idx = differences.nlargest(2).index.min()
    
  • 如果您知道所有小值都分组在一起(如示例中的所有 0.1),您可以过滤大于均值的值(或者如果您的“大”值非常接近较小的)。

    idx = differences.index[differences >= differences.mean()][0]
    

    这是因为与中位数相反,您的少数较大差异会显着提高均值。

如果您真的想进行适当的聚类,可以使用 scikit learn 中的 KMeans 算法:

from sklearn.cluster import KMeans

kmeans = KMeans(n_clusters=2).fit(differences.values[:-1].reshape(-1, 1))
clusters = pd.Series(kmeans.labels_, index=differences.index[:-1])
idx = clusters.index[clusters.eq(np.squeeze(kmeans.cluster_centers_).argmax())][0]

这会将数据分为 2 类,然后将分类放入 pandas Series。然后我们通过只选择具有最高值的集群来过滤这个系列的索引,最后得到这个过滤索引的第一个元素。

【讨论】:

  • 有道理...我也会研究集群的 scikit KMeans 算法。感谢您的澄清。
  • 我发现向平均值添加 3 个标准差对我的示例案例有效。如果有几个大的差距,这有点道理,我们假设一个相当正态的分布。
  • 我也喜欢你关于集群的想法。这很有趣,因为我试图找到用于谱聚类的 k 估计的“第一个大”特征间隙。
【解决方案2】:

一个想法是通过Series.quantile 过滤一系列差异,通过-1 和绝对值更改差异顺序,最后获得第一个索引:

df = pd.DataFrame({'A':[1, 1.1, 1.2, 1.3, 1.4, 1.5, 7, 7.1, 7.2, 15, 15.1]})


x = df['A'].diff(-1) .abs()
print (x)
0     0.1
1     0.1
2     0.1
3     0.1
4     0.1
5     5.5
6     0.1
7     0.1
8     7.8
9     0.1
10    NaN
Name: A, dtype: float64

idx = x.index[x >= x.quantile(.75)]
print (idx)
Int64Index([5, 7, 8], dtype='int64')

print (idx[0])
5

【讨论】:

  • 如果数据有负值,.abs() 会影响函数吗?
  • @BobTomato - 嗯,这里使用了abs 的差异,原因是通过quantile 仅比较正差异。所以Would the .abs() affect the function if the data had negative values?不容易回答,因为它取决于diff方法的输出。
  • @BobTomato abs 很重要,您的值没有排序,但值本身的符号无关紧要。
  • @jezrael 我明白了...我尝试过使用分位数,它会根据数据输出不同的结果,因此无法真正概括。
【解决方案3】:

如果你有一个 Numpy 数组,你可以使用任何数据框行,你可以使用numpy.argmax

import numpy as np

import numpy as np

a = np.array([1, 1.1, 1.2, 1.3, 1.4, 1.5, 7, 7.1, 7.2, 15, 15.1])
diff = np.diff(a)
threshold = 2 # set your threshold
max_index = np.argwhere(diff> threshold) [[5],[8]]

参考资料: https://numpy.org/doc/stable/reference/generated/numpy.diff.html https://numpy.org/doc/stable/reference/generated/numpy.argwhere.html

更多信息:

pandas.diff 将计算差异 diff[i] = a[i] - a[i-1]
numpy.diff 将计算差异 diff[i] = a[i+1] - a[i],
除了 i=max len:

  • a[i] = a[i]-a[i-1]

【讨论】:

    【解决方案4】:
    def shift(a):
      
      a_r = np.roll(a, 1) # right shift
      a_l = np.roll(a, -1) # left shift
    
    
      return np.stack([a_l, a_r], axis=1)
    

    a = np.array([1, 1.1, 1.2, 1.3, 1.4, 1.5, 7, 7.1, 7.2, 15, 15.1])
    

    diff = abs(shift(a) - a.reshape(-1, 1))
    
    diff = diff[1:-1]
    
    indices = diff.argmax(axis=0) - 2
    
    a[indices]
    

    array([7. , 1.5])
    

    【讨论】:

      猜你喜欢
      • 2021-09-13
      • 2017-12-30
      • 2020-08-17
      • 1970-01-01
      • 2017-05-06
      • 2021-11-10
      • 1970-01-01
      • 2022-08-04
      • 2016-11-08
      相关资源
      最近更新 更多