【问题标题】:Replacing part of a plot with a dotted line用虚线替换部分绘图
【发布时间】:2018-06-18 20:54:30
【问题描述】:

我想用从前一点继续的虚线替换我的绘图中函数下降到“-1”的部分(见下图)。

这是我编写的一些代码及其输出:

import numpy as np

import matplotlib.pyplot as plt
y = [5,6,8,3,5,7,3,6,-1,3,8,5]

plt.plot(np.linspace(1,12,12),y,'r-o')
plt.show()

for i in range(1,len(y)):
    if y[i]!=-1:
        plt.plot(np.linspace(i-1,i,2),y[i-1:i+1],'r-o')
    else:
        y[i]=y[i-1]
        plt.plot(np.linspace(i-1,i,2),y[i-1:i+1],'r--o')
plt.ylim(-1,9)
plt.show()

这是原图

修改后的情节:

我编写的代码有效(它会产生所需的输出),但是当我在(更大的)数据集上实际运行它时,它效率低下并且需要很长时间。有没有更聪明的方法来做这件事?

【问题讨论】:

  • 所以你假设第一个值永远不是-1?
  • 一个可能的想法是使用掩码数组,例如y_masked = np.ma.masked_where(y == -1, y)。绘制它会错过-1 值,所以你仍然需要做一些工作来显示以前的值并添加一条虚线(可能只绘制屏蔽值)。

标签: python numpy matplotlib plot


【解决方案1】:

您可以在没有循环的情况下实现类似的效果:

import pandas as pd
import matplotlib.pyplot as plt

# Create a data frame from the list
a = pd.DataFrame([5,6,-1,-1, 8,3,5,7,3,6,-1,3,8,5])

# Prepare a boolean mask
mask = a > 0

# New data frame with missing values filled with the last element of   
# the previous segment. Choose 'bfill' to use the first element of 
# the next segment.
a_masked = a[mask].fillna(method = 'ffill')

# Prepare the plot
fig, ax = plt.subplots()
line, = ax.plot(a_masked, ls = '--', lw = 1)
ax.plot(a[mask], color=line.get_color(), lw=1.5, marker = 'o')
plt.show()

您还可以通过为线条选择不同的颜色来突出显示负面区域:

我的回答基于 2017 年 7 月的一篇精彩文章。后者还解决了第一个元素为 NaN 或在您的情况下为负数的情况: Dotted lines instead of a missing value in matplotlib

【讨论】:

  • 谢谢!有没有办法让它完全一样?我更喜欢这个答案,因为它不使用循环。当有很长的 -1 字符串时,上面的结果会非常慢。
  • 知道了 - 我会在几分钟后发布我自己的解决方案
【解决方案2】:

我会使用numpy 功能将您的线切割成段,然后分别绘制所有实线和虚线。在下面的示例中,我向您的数据添加了两个额外的-1s,以查看这是否普遍适用。

import numpy as np
import matplotlib.pyplot as plt

Y = np.array([5,6,-1,-1, 8,3,5,7,3,6,-1,3,8,5])
X = np.arange(len(Y))

idxs =  np.where(Y==-1)[0]

sub_y = np.split(Y,idxs)
sub_x = np.split(X,idxs)

fig, ax = plt.subplots()

##replacing -1 values and plotting dotted lines
for i in range(1,len(sub_y)):
    val = sub_y[i-1][-1]
    sub_y[i][0] = val
    ax.plot([sub_x[i-1][-1], sub_x[i][0]], [val, val], 'r--')

##plotting rest
for x,y in zip(sub_x, sub_y):
    ax.plot(x, y, 'r-o')

plt.show()

结果如下:

但是请注意,如果第一个值是-1,这将失败,因为您的问题没有很好地定义(没有以前的值可以复制)。希望这会有所帮助。

【讨论】:

  • 我知道我已经接受了答案,但是有没有办法在不使用循环的情况下做到这一点?当我有很长的 -1 列表时,这被证明是非常昂贵的
  • @Lupacante 我认为另一个答案实际上是更好的解决方案。稍加调整,你应该能够得到你所追求的。哦,如果您改变主意并接受另一个答案,那完全没有问题:)
  • 谢谢!好的,我看看我能做什么
【解决方案3】:

不太优雅,但这里有一些不使用我想出的循环(基于上述答案)的东西。 @KRKirov 和 @Thomas Kühn,感谢您的回答,我非常感谢他们

import pandas as pd
import matplotlib.pyplot as plt

# Create a data frame from the list
a = pd.DataFrame([5,6,-1,-1, 8,3,5,7,3,6,-1,3,8,5])

b=a.copy()
b[2]=b[0].shift(1,axis=0)
b[4]=(b[0]!=-1) & (b[2]==-1)
b[5]=b[4].shift(-1,axis=0)
b[0] = (b[5] | b[4])
c=b[0]
d=pd.DataFrame(c)

# Prepare a boolean mask
mask = a > 0

# New data frame with missing values filled with the last element of   
# the previous segment. Choose 'bfill' to use the first element of 
# the next segment.
a_masked = a[mask].fillna(method = 'ffill')

# Prepare the plot
fig, ax = plt.subplots()
line, = ax.plot(a_masked, 'b:o', lw = 1)
ax.plot(a[mask], color=line.get_color(), lw=1.5, marker = 'o')
ax.plot(a_masked[d], color=line.get_color(), lw=1.5, marker = 'o')
plt.show()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多