查找图的峰和谷并标记它们

时间:2018-09-01 05:41:01

标签: python pandas plot

      df1
      Date           Topic  Return
      1/1/2010        A,B     -0.308648967
      1/2/2010        C,D     -0.465862046
      1/3/2010        E        0.374052392
      1/4/2010        F        0.520312204
      1/5/2010        G        0.503889198
      1/6/2010        H       -1.730646788
      1/7/2010        L,M,N    1.756295613
      1/8/2010        K        -0.598990239
      ......
      1/30/2010       z         2,124355
 Plot= df1.plot(x='Date', y='Return')

如何找到该图的最高峰和最低谷,并用相应的主题标记这些特殊点?

2 个答案:

答案 0 :(得分:1)

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# Take an example data
data = {"Date":["date{i}".format(i=i) for i in range(10)], "Topic":["topic{i}".format(i=i) for i in range(10)], "Return":[1,2,3,2,1,2,4,7,1,3]}
df = pd.DataFrame.from_dict(data)

dates = np.array(df["Date"].tolist())
returns = np.array(df["Return"].tolist())

# Calculate the minimas and the maximas
minimas = (np.diff(np.sign(np.diff(returns))) > 0).nonzero()[0] + 1 
maximas = (np.diff(np.sign(np.diff(returns))) < 0).nonzero()[0] + 1

# Plot the entire data first
plt.plot(dates, returns)
# Then mark the maximas and the minimas
for minima in minimas:
    plt.plot(df.iloc[minima]["Date"], df.iloc[minima]["Return"], marker="o", label=str(df.iloc[minima]["Topic"]))
for maxima in maximas:
    plt.plot(df.iloc[maxima]["Date"], df.iloc[maxima]["Return"], marker="o", label=str(df.iloc[maxima]["Topic"]))

plt.legend()
plt.show()

示例数据框:

   Date   Topic  Return
0  date0  topic0       1
1  date1  topic1       2
2  date2  topic2       3
3  date3  topic3       2
4  date4  topic4       1
5  date5  topic5       2
6  date6  topic6       4
7  date7  topic7       7
8  date8  topic8       1
9  date9  topic9       3

它产生的图: doc

答案 1 :(得分:1)

这一点取决于您对“峰值”和“谷”的定义。通常,一个人可能会关心平滑的波峰和波谷,以识别广泛的趋势,尤其是在有噪音的情况下。如果您希望每个细粒度的数据下降或上升(并且如果对行进行了排序),则可以使用numpy中的矢量化例程作弊。

import numpy as np

d = np.diff(df['Return'])
i = np.argwhere((d[:-1]*d[1:])<=0).flatten()
special_points = df['Topic'][i+1]

带有np.diff()的第一行将每个返回值与下一个返回值进行比较。特别地,它减去它们。在某种程度上取决于您对局部峰/谷的定义,这些属性将具有以下特性:如果这些成对差异的符号交替出现,则您仅具有要查找的功能。考虑下面的峰值。

[1, 5, 1]

如果计算成对差异,则向量会略短

[4, -4]

请注意,这些符号交替出现。因此,如果将它们相乘,将得到-16,它必须为负数。这是我们的代码用来识别峰和谷的确切见解。降维会稍微抵消一些东西,因此我们将找到的索引移位1(在df['Topic'][i+1]块中)。

注意事项:请注意,我们有<=而不是严格的不等式。这是在我们的峰比正常峰宽的情况下。考虑[1, 2, 2, 2, 2, 2, 1]。可以说,字符串2代表一个峰值,需要捕获。如果不希望如此,则使不等式严格。

此外,如果您对这样的宽峰感兴趣,则此算法仍然不正确。它非常快,但是通常只计算峰/谷的超集。考虑以下

[1, 2, 2, 3, 2, 1]

可以说,数字3是该数据集中的唯一峰值(当然,这在一定程度上取决于您的定义),但是我们的算法也会由于数字2的第一个和第二个实例位于架子上(与邻居相同)。

其他scipy.signal模块具有多种峰值查找算法,根据您对峰值的任何其他要求,它们可能更适合。修改此解决方案不可能像使用适当的内置信号处理器一样快或干净。调用scipy.signal.find_peaks()基本上可以复制我们在这里所做的一切,如果需要,它还有更多选择。如果需要任何平滑或更复杂的操作,则像scipy.signal.find_peaks_cwt()之类的其他算法可能更合适。