简体   繁体   English

求曲线下的最大面积| 熊猫,matplotlib

[英]Finding the largest area under a curve | pandas, matplotlib

I am struggling a bit in this one - in order to find the necessary battery capacity I need to analyze production/demand over a year's worth of data.我在这方面有点挣扎 - 为了找到必要的电池容量,我需要分析一年的数据生产/需求。 To do this, I figured I need to calculate the biggest area under the 0 line.为此,我想我需要计算 0 线下的最大面积。 I guess I need to find the start/end points of that area and multiply everything by its respective y-value?我想我需要找到该区域的起点/终点并将所有内容乘以各自的 y 值?

Here is a shortened version of the graph I have:这是我拥有的图表的缩短版本: 在此处输入图片说明 That is the biggest area under the 0 in the image, but in the full dataset it could be any area.这是图像中 0 下最大的区域,但在完整数据集中,它可以是任何区域。 I know how to integrate it in the case I find the boundaries of the area in question but I'm struggling to find an efficient way to do that.我知道如何将它整合到我找到相关区域边界的情况下,但我正在努力寻找一种有效的方法来做到这一点。

My dataframe looks like this:我的数据框如下所示:

                     demand  Production    diff
Time
2019-01-01 00:15:01   17.25      32.907  15.657
2019-01-01 00:30:01   17.80      32.954  15.154
...                     ...         ...     ...
2019-01-16 22:15:02   17.34      27.704  10.364
2019-01-16 22:30:01   18.67      35.494  16.824

I use this snippet to find the length in timesteps of the longest area but I'm missing if there's a way to multiply the points by their y-values (diff).我使用这个片段来找到最长区域的时间步长,但是如果有办法将点乘以它们的 y 值(差异),我就找不到了。 It's technically not correct , however, considering that an area might be long but narrow and another might be shorter and taller, so with an overall bigger area.然而,这在技术上是不正确的,考虑到一个区域可能长而窄,另一个区域可能更短更高,因此整体面积更大。

def max0(sr):
     return (sr >= 0).cumsum().value_counts().max() - (0 if (sr >= 0).cumsum().value_counts().idxmax() < 0 else 1)

You can find the largest area under the 0-line.您可以找到 0 线下最大的区域。 I generated my own data我生成了自己的数据

x = np.random.randn(100000)
x = x.cumsum()-x.mean()
plt.plot(x);

样本数据

Now calculate the start and end points for positive and negative sequences.现在计算正序列和负序列的起点和终点。 Every value in a sequence gets an increasing integer to be able to group by sequence.序列中的每个值都有一个递增的整数,以便能够按序列分组。

x1 = np.diff(x < 0).cumsum()

Use pandas groupby to compute all areas and find the largest negative使用 pandas groupby 计算所有区域并找到最大的负数

df = pd.DataFrame({
    'value': x[1:],
    'border': x1
})
dfg = df.groupby('border')
mingr = dfg.apply(lambda x: np.trapz(x.value)).idxmin()
plt.plot(x[1:])
plt.plot(
    dfg.get_group(mingr).value
);
plt.title(
    "position from {} to {}".format(
        dfg.get_group(mingr).index[0],
        dfg.get_group(mingr).index[-1]));

0线下最大面积

How this works这是如何工作的

I create a dataset that is easier to follow along我创建了一个更容易遵循的数据集

x = np.array([3,4,4.5,3,2])
X = np.r_[x,-x,x,-x]+np.random.normal(0,.2,20)
plt.figure(figsize=(12,5))
plt.axhline(0, color='gray')
plt.plot(X, 'o--');

数据集

I want to know the sequences with consecutive negative or positive values.我想知道具有连续负值或正值的序列。 This can be archived with the filter X < 0.这可以使用过滤器 X < 0 进行存档。

df = pd.DataFrame({'value': X, 'lt_zero': X < 0})
df[:10]
      value  lt_zero
0  3.125986    False
1  3.885588    False
2  4.580410    False
3  2.998920    False
4  1.913088    False
5 -2.902447     True
6 -3.986654     True
7 -4.373026     True
8 -2.878661     True
9 -1.929964     True

Now I can find the indices where the sign changes, when I diff every consecutive value.现在,当我区分每个连续值时,我可以找到符号发生变化的索引。 I concat one False before the data to not loose the first value.我在数据之前连接一个 False 以不丢失第一个值。

df['sign_switch'] = np.diff(np.r_[False, X < 0])
df[:10]
      value  lt_zero  sign_switch
0  3.125986    False        False
1  3.885588    False        False
2  4.580410    False        False
3  2.998920    False        False
4  1.913088    False        False
5 -2.902447     True         True
6 -3.986654     True        False
7 -4.373026     True        False
8 -2.878661     True        False
9 -1.929964     True        False

With cumsum() I get for every sequence an increasing integer value.使用cumsum()我为每个序列得到一个递增的整数值。 Now I have a grouping variable for every sequence.现在我有每个序列的分组变量。

df['sign_sequence'] = np.diff(np.r_[False, X < 0]).cumsum()
df[:10]
      value  lt_zero  sign_switch  sign_sequence
0  3.125986    False        False              0
1  3.885588    False        False              0
2  4.580410    False        False              0
3  2.998920    False        False              0
4  1.913088    False        False              0
5 -2.902447     True         True              1
6 -3.986654     True        False              1
7 -4.373026     True        False              1
8 -2.878661     True        False              1
9 -1.929964     True        False              1

For every group I can compute the integral for the values in the group.对于每个组,我可以计算组中值的积分。

sign_groups = df.groupby('sign_sequence')
sign_groups.apply(lambda x: np.trapz(x.value))
sign_sequence
0    13.984455
1   -13.654547
2    14.370044
3   -14.549090

You can access every group later and use the areas.您可以稍后访问每个组并使用这些区域。 For example to plot the areas.例如绘制区域。

plt.figure(figsize=(12,5))
plt.plot(X,'o--')
plt.axhline(0, c='gray')
for e,group in enumerate(sign_groups):
    plt.fill_between(group[1].index,0, group[1].value)
    area = np.trapz(group[1].value)
    plt.text((e)*5+1.5, np.sign(area) * 1.25, f'{area:.2f}', fontsize=12)

用区域绘图

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM