用虚线替换图表中的一部分

4
我希望将图表中函数下降到“-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()

这是原始情节:

original plot

修改后的情节:

modified plot

我编写的代码可以工作(它产生了所需的输出),但在实际运行时效率低下,处理我的(更大的)数据集需要很长时间。有没有更聪明的方法来做到这一点?


那么你认为第一个值永远不会是-1? - mikuszefski
1
一个可能的想法是使用掩码数组,例如 y_masked = np.ma.masked_where(y == -1, y)。绘制这个图表会忽略 -1 值,因此您仍然需要做一些工作来显示先前的值并添加虚线(也许只绘制掩码值)。 - Ed Smith
3个回答

3

您可以不使用循环实现类似的功能:

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()

enter image description here

你可以通过选择不同的颜色来突出显示负区域的线条: 输入图像描述 我的答案基于2017年7月的一篇很棒的文章。该文章还解决了第一个元素为NaN或在你的情况下为负数的情况: 使用虚线代替matplotlib中缺失的值

谢谢!有没有办法完全相同?我更喜欢这个答案,因为它不使用循环。当有长串的-1时,上面的方法变得非常慢。 - ignoring_gravity
明白了 - 我会在几分钟内发布我的解决方案。 - ignoring_gravity

2
我会使用numpy的功能将您的线条分割成段,然后分别绘制所有实线和虚线。在下面的示例中,我向您的数据添加了两个额外的-1,以便查看这是否通用。
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()

结果如下所示:

enter image description here

请注意,如果第一个值为-1,则此方法将失败,因为您的问题没有定义良好(没有先前的值可复制)。希望这可以帮助到您。

我知道我已经接受了答案,但是有没有不使用循环的方法来完成这个任务?当我有很长的-1列表时,使用循环会变得非常昂贵。 - ignoring_gravity
1
@Lupacante 我认为另一个回答实际上是更好的解决方案。稍作调整,你应该能够得到你想要的结果。哦,如果你改变主意并接受另一个答案,也没有问题 :) - Thomas Kühn
谢谢!好的,我会看看我能做什么。 - ignoring_gravity

0

这里有一个不太优雅的解决方案,但它不使用循环(基于上面的答案)。@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()

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