之前已经问过这个问题的各种版本,我不确定我是否应该在其中一个主题上提出我的问题或者开始一个新主题。这是:
我有一个pandas数据框,其中有一个我想要绘制的列(例如:speed),然后是另一列(例如:active),现在是true / false。根据活动的值,我想为线图着色。
这个主题似乎是“正确的”解决方案,但我遇到了一个问题: seaborn or matplotlib line chart, line color depending on variable OP和我正在努力实现同样的目标:
这是一个破碎的情节/再现者:
Values=[3,4,6, 6,5,4, 3,2,3, 4,5,6]
Colors=['red','red', 'red', 'blue','blue','blue', 'red', 'red', 'red', 'blue', 'blue', 'blue']
myf = pd.DataFrame({'speed': Values, 'colors': Colors})
grouped = myf.groupby('colors')
fig, ax = plt.subplots(1)
for key, group in grouped:
group.plot(ax=ax, y="speed", label=key, color=key)
结果图有两个问题:不仅更改的颜色线不是“连接”,而且颜色本身连接“跨越”端点:
我想看到的是从红色到蓝色的变化,看起来像是一条连续的线。
Color line by third variable - Python似乎做对了,但我没有处理“线性”颜色数据。我基本上是在一列中分配一组线条颜色。我可以轻松地将颜色列的值设置为数字:
Colors=['1','1', '1', '2','2'...]
如果这样可以更容易地生成所需的绘图。
第一个帖子中有评论:
如果你在颜色改变时重复点,你可以这样做,我已经 修改后的答案
但我基本上复制并粘贴了答案,所以我不确定评论是否完全准确。
答案 0 :(得分:2)
我对它采取了一个裂缝。根据您链接的其他问题中的评论,将我引导至this。我确实必须开始使用matplotlib并且不能在熊猫本身做到这一点。将数据框转换为列表后,其代码与mpl page中的代码完全相同。
我创建的数据框与您的类似:
vals=[3,4,6, 6,5,4, 3,2,3, 4,5,6]
colors=['red' if x < 5 else 'blue' for x in vals]
df = pd.DataFrame({'speed': vals, 'danger': colors})
将vals和index转换为列表
x = df.index.tolist()
y = df['speed'].tolist()
z = np.array(list(y))
将vals和index分解为点,然后创建线段 他们之外。
points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
根据创建数据框时使用的条件创建色彩映射表。在我的情况下,小于5的速度是红色,休息是蓝色。
cmap = ListedColormap(['r', 'b'])
norm = BoundaryNorm([0, 4, 10], cmap.N)
创建线段并相应地指定颜色
lc = LineCollection(segments, cmap=cmap, norm=norm)
lc.set_array(z)
剧情!
fig = plt.figure()
plt.gca().add_collection(lc)
plt.xlim(min(x), max(x))
plt.ylim(0, 10)
这是输出:
注意:在当前代码中,线段的颜色取决于起点。但希望这会给你一个想法。
我在这里回答问题还是新手。如果我需要添加/删除一些细节,请告诉我。谢谢!
答案 1 :(得分:1)
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
Values=[3,4,6, 6,5,4, 3,2,3, 4,5,6]
Colors=['red','red', 'red', 'blue','blue','blue', 'red', 'red', 'red', 'blue', 'blue', 'blue']
myf = pd.DataFrame({'speed': Values, 'colors': Colors})
myf['change'] = myf.colors.ne(myf.colors.shift().bfill()).astype(int)
myf['subgroup'] = myf['change'].cumsum()
myf
colors speed change subgroup
0 red 3 0 0
1 red 4 0 0
2 red 6 0 0
3 blue 6 1 1
4 blue 5 0 1
5 blue 4 0 1
6 red 3 1 2
7 red 2 0 2
8 red 3 0 2
9 blue 4 1 3
10 blue 5 0 3
11 blue 6 0 3
myf.index += myf['subgroup'].values
myf
colors speed change subgroup
0 red 3 0 0
1 red 4 0 0
2 red 6 0 0
4 blue 6 1 1 # index is now 4; 3 is missing
5 blue 5 0 1
6 blue 4 0 1
8 red 3 1 2 # index is now 8; 7 is missing
9 red 2 0 2
10 red 3 0 2
12 blue 4 1 3 # index is now 12; 11 is missing
13 blue 5 0 3
14 blue 6 0 3
first_i_of_each_group = myf[myf['change'] == 1].index
first_i_of_each_group
Int64Index([4, 8, 12], dtype='int64')
for i in first_i_of_each_group:
# Copy next group's first row to current group's last row
myf.loc[i-1] = myf.loc[i]
# But make this new row part of the current group
myf.loc[i-1, 'subgroup'] = myf.loc[i-2, 'subgroup']
# Don't need the change col anymore
myf.drop('change', axis=1, inplace=True)
myf.sort_index(inplace=True)
# Create duplicate indexes at each subgroup border to ensure the plot is continuous.
myf.index -= myf['subgroup'].values
myf
colors speed subgroup
0 red 3 0
1 red 4 0
2 red 6 0
3 blue 6 0 # this and next row both have index = 3
3 blue 6 1 # subgroup 1 picks up where subgroup 0 left off
4 blue 5 1
5 blue 4 1
6 red 3 1
6 red 3 2
7 red 2 2
8 red 3 2
9 blue 4 2
9 blue 4 3
10 blue 5 3
11 blue 6 3
fig, ax = plt.subplots()
for k, g in myf.groupby('subgroup'):
g.plot(ax=ax, y='speed', color=g['colors'].values[0], marker='o')
ax.legend_.remove()