Pandas找到本地最大值和最小值

54

我有一个pandas数据框,包含两列,一列是温度,另一列是时间。

我想要创建第三列和第四列,分别称为“min”和“max”。每个列中都填充有nan,除非存在局部最小值或最大值,那么它将具有该极值的值。

以下是数据样本,本质上我正在尝试识别图表中的所有峰值和低点。

输入图像描述

pandas中是否有任何内置工具可以实现此目的?


结果是否应该对噪声具有鲁棒性?否则,您可以将系列的值与其移位进行比较。 - fuglede
在这种情况下,我不担心噪音,如果是嘈杂的信号,我会先进行滤波,然后在滤波结果中寻找最大/最小值。 - Mustard Tiger
1
你可以选择对数据拟合一个非常简单的模型(例如,只有一个或两个协变量的线性模型),然后从残差项中保留那些偏差在q%最小或最大类别中的项,使用pd.quantile函数。 - Nelewout
4个回答

124

fuglede提供的解决方案很好,但如果你的数据非常嘈杂(像图片中的数据),你最终会得到很多误导性的局部极值。我建议您使用scipy.signal.argrelextrema()方法。 .argrelextrema()方法有自己的限制,但它具有一个有用的特性,您可以指定要比较的点数,就像一个噪声过滤算法。例如:

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from scipy.signal import argrelextrema

# Generate a noisy AR(1) sample

np.random.seed(0)
rs = np.random.randn(200)
xs = [0]
for r in rs:
    xs.append(xs[-1] * 0.9 + r)
df = pd.DataFrame(xs, columns=['data'])

n = 5  # number of points to be checked before and after

# Find local peaks

df['min'] = df.iloc[argrelextrema(df.data.values, np.less_equal,
                    order=n)[0]]['data']
df['max'] = df.iloc[argrelextrema(df.data.values, np.greater_equal,
                    order=n)[0]]['data']

# Plot results

plt.scatter(df.index, df['min'], c='r')
plt.scatter(df.index, df['max'], c='g')
plt.plot(df.index, df['data'])
plt.show()

一些要点:

  • 您可能需要检查点之后,以确保没有非常接近的细节。
  • 您可以尝试使用n来过滤噪点
  • argrelextrema返回一个元组,最后的[0]提取出一个numpy数组

11
这是一个不错的解决方案。我写了一篇小博客介绍它:http://eddwardo.github.io/pandas/timeseries/2019/06/05/finding-local-extreams-in-pandas-time-series/ - eddd
2
非常棒的博客文章@eddd,真的帮助我理解了它! - Rob H
2
@eddd,页面已经崩溃了。 - Foad S. Farimani
6
好的,请提供需要翻译的内容。 - eddd
2
最佳解决方案也是最快的。不知道argrelextrema - linello
1
很好的想法,但请注意,当最小值/最大值在数值上完全相同时,此解决方案似乎存在问题。例如,np.less_equal 可能会检测到它们所有,而 np.less 则可能根本不会检测到它们。请参见此问题 - bluenote10

55

假设感兴趣的列标记为data,一种解决方案是:

df['min'] = df.data[(df.data.shift(1) > df.data) & (df.data.shift(-1) > df.data)]
df['max'] = df.data[(df.data.shift(1) < df.data) & (df.data.shift(-1) < df.data)]
例如:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

# Generate a noisy AR(1) sample
np.random.seed(0)
rs = np.random.randn(200)
xs = [0]
for r in rs:
    xs.append(xs[-1]*0.9 + r)
df = pd.DataFrame(xs, columns=['data'])

# Find local peaks
df['min'] = df.data[(df.data.shift(1) > df.data) & (df.data.shift(-1) > df.data)]
df['max'] = df.data[(df.data.shift(1) < df.data) & (df.data.shift(-1) < df.data)]

# Plot results
plt.scatter(df.index, df['min'], c='r')
plt.scatter(df.index, df['max'], c='g')
df.data.plot()

enter image description here


1
我发现当数据的值重复时,例如多行具有值7,仅使用<或>会将数据点视为“最小值”或“最大值”。修改此解决方案以使用“.shift(1)<=”和“.shift(1)> =”实际上允许识别重复值的“最小值”和“最大值”。逻辑是包含重复值的最后一行将被视为“最小值”或“最大值”。 - Udesh
伟大的发现,Udesh。 - sam
很棒的解决方案! - Sepide

5
您可以使用Pandas的.rolling()函数做类似于Foad's .argrelextrema() solution的事情:
# Find local peaks
n = 5 #rolling period
local_min_vals = df.loc[df['data'] == df['data'].rolling(n, center=True).min()]
local_max_vals = df.loc[df['data'] == df['data'].rolling(n, center=True).max()]

plt.scatter(local_min_vals.index, local_min_vals, c='r')
plt.scatter(local_max_vals.index, local_max_vals, c='g')

Image of a noisy signal with red dots showing local minima and green dots showing local maxima.


3

使用Numpy

ser = np.random.randint(-40, 40, 100) # 100 points
peak = np.where(np.diff(ser) < 0)[0]

或者

double_difference = np.diff(np.sign(np.diff(ser)))
peak = np.where(double_difference == -2)[0]

使用Pandas

ser = pd.Series(np.random.randint(2, 5, 100))
peak_df = ser[(ser.shift(1) < ser) & (ser.shift(-1) < ser)]
peak = peak_df.index

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接