Matplotlib PathPatch 颜色和图例不匹配
Matplotlib PathPatch Colors and Legends not Matching
我有一个数据集,它是一个列表列表。
每个列表都是一个要绘制为箱线图的类别。
每个列表都有一个最多包含 9 个要绘制成子图的组件的列表。
下面我使用的函数是基于这个answer。我把它从我的工作中拉出来并添加了一些模拟数据。下面应该是一个最小的例子。
neonDict = {
0:0, 1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7, 8:8
}
import matplotlib as mpl
import matplotlib.pyplot as plt
def coloredBoxPlot(axis, data,edgeColor,fillColor):
bp = axis.boxplot(data,vert=False,patch_artist=True)
for element in ['boxes', 'whiskers', 'fliers', 'means', 'medians', 'caps']:
plt.setp(bp[element], color=edgeColor)
for patch in bp['boxes']:
patch.set(facecolor=fillColor)
return bp
def plotCalStats(data, prefix='Channel', savedir=None,colors=['#00597c','#a8005c','#00aeea','#007d50','#400080','#e07800'] ):
csize = mpl.rcParams['figure.figsize']
cdpi = mpl.rcParams['figure.dpi']
mpl.rcParams['figure.figsize'] = (12,8)
mpl.rcParams['figure.dpi'] = 1080
pkdata = []
labels = []
lstyles = []
fg, ax = plt.subplots(3,3)
for pk in range(len(neonDict)):
px = pk // 3
py = pk % 3
ax[px,py].set_xlabel('Max Pixel')
ax[px,py].set_ylabel('')
ax[px,py].set_title(str(neonDict[pk]) + ' nm')
pkdata.append([])
for cat in range(len(data)):
bp = ''
for acal in data[cat]:
for apeak in acal.peaks:
pkdata[apeak].append(acal.peaks[apeak][0])
for pk in range(9):
px = pk // 3
py = pk % 3
bp = coloredBoxPlot(ax[px,py], pkdata[pk], colors[cat], '#ffffff')
if len(data[cat]) > 0:
#print(colors[cat])
#print(bp['boxes'][0].get_edgecolor())
labels.append(prefix+' '+str(cat))
lstyles.append(bp['boxes'][0])
fg.legend(lstyles,labels)
fg.suptitle('Calibration Summary by '+prefix)
fg.tight_layout()
if savedir is not None:
plt.savefig(savedir + 'Boxplots.png')
plt.show()
mpl.rcParams['figure.figsize'] = csize
mpl.rcParams['figure.dpi'] = cdpi
return
class acal:
def __init__(self):
self.peaks = {}
for x in range(9):
self.peaks[x] = (np.random.randint(20*x,20*(x+1)),)
mockData = [[acal() for y in range(100)] for x in range(6)]
#Some unused channels
mockData[2] = []
mockData[3] = []
mockData[4] = []
plotCalStats(mockData)
所以问题是绘图颜色与图例不符。即使我将数据限制为仅在数据存在时添加标签(因此确保使用空数据集调用箱线图并且没有获得适当的 PathPatch 没有问题。
打印输出验证颜色是否正确存储在 PathPatch 中。 (我可以添加我的数字 -> 十六进制转换器)如果有疑问。
附件是输出。可以看到我得到了一个紫色的盒子,但传说中没有紫色。紫色是第 4 个类别,它是空的。
知道标签与实际样式不符的原因吗?非常感谢!
编辑:
解决关于 'confusing' 的问题。
我有六类数据,每一类都来自一个事件。每个事件有 9 个组成部分。我想比较所有事件,对于每个单独的组件,对于单个图上的每个类别,如下所示。
每个子图都是一个单独的组件,由每个分类(通道)的数据系列组成。
所以我提供的 link(就像我说的,改编自)展示了如何在一个轴上为 2 个数据集创建一个箱线图。我基本上对 9 轴上的 6 个数据集做了同样的事情,其中 3 个数据集是空的(但不必如此,我这样做是为了说明问题。如果我有所有 6 个数据集,怎么能你说颜色乱了?????)
关于阿尔法:
仅向 matplotlib 提供 RGB 数据时,alpha 总是 'ff'。如果我调用 get_edgecolors,它将 return 一个元组 (RGBA),其中 A = 1.0。
请参阅注释掉的打印语句。
编辑 2:
如果我将其限制为单个类别,则箱形图视图不会那么混乱。
单个示例(查看箱形图颜色如何为橙色,图中显示为蓝色)
所有颜色关闭
感觉这个曾经有用....
不确定错误是如何出现的,但问题与在创建箱形图之前重新格式化数据有关。
通过在循环类别之前创建子图期间删除 pkdata.append([])
并添加:
pkdata = [[],[],[],[],[],[],[],[],[]]
在类别循环的每次迭代中修复了该问题。前者正在发送所有以前的频道数据...
输出现在更好了。附上完整的 sol。
可能,因为绘图使用来自 pkdata 的数据,空通道 (data[cat]) 绘制了以前的数据(来自 data[cat-1]),因为它仍然在 pkdata 中(实际上,所有以前的数据 [cat] ] 仍在 pkdata 中)然后被绘制。我只检查 data[cat] 以获取每个循环中的数据以添加到图例中。例如,图例是为通道 0、1、5 设置的。但是我们看到通道的数据:0 为 0,0+1 为 1,0+1 为 2,0+1 为 3,0+1 为4、0+1+5 为 5... 因此通道 4(紫色)有要绘制的数据,但未添加到图例中。给人的印象是 'misaligned' 传奇,而不是非传奇数据...
单通道数据实际上是所有6通道重叠,最后通道5颜色为橙色,重叠所有之前的数据,即数据所属的原始通道0数据并适当添加到图例中。
neonDict = {
0:0, 1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7, 8:8
}
import matplotlib as mpl
import matplotlib.pyplot as plt
def getHex(r,g,b,a=1.0):
colors = [int(r * 255 ),int(g * 255 ),int(b * 255 ),int(a * 255) ]
s = '#'
for x in range(4):
cs = hex(colors[x])
if len(cs) == 3:
cs = cs + '0'
s += cs.replace('0x','')
return s
def getRGB(colstr):
try:
a = ''
r = int(colstr[1:3],16) / 255
g = int(colstr[3:5],16) / 255
b = int(colstr[5:7],16) / 255
if len (colstr) == 7:
a = 1.0
else:
a = int(colstr[7:],16) / 255
return (r,g,b,a)
except Exception as e:
print(e)
raise e
return
def compareHexColors(col1,col2):
try:
## ASSUME #RBG or #RBGA
## If less than 7, append the ff for the colors
if len(col1) < 9:
col1 += 'ff'
if len(col2) < 9:
col2 += 'ff'
return col1.lower() == col2.lower()
except Exception as e:
raise e
return False
def coloredBoxPlot(axis, data,edgeColor,fillColor):
bp = axis.boxplot(data,vert=False,patch_artist=True)
for element in ['boxes', 'whiskers', 'fliers', 'means', 'medians', 'caps']:
plt.setp(bp[element], color=edgeColor)
for patch in bp['boxes']:
patch.set(facecolor=fillColor)
return bp
def plotCalStats(data, prefix='Channel', savedir=None,colors=['#00597c','#a8005c','#00aeea','#007d50','#400080','#e07800'] ):
csize = mpl.rcParams['figure.figsize']
cdpi = mpl.rcParams['figure.dpi']
mpl.rcParams['figure.figsize'] = (12,8)
mpl.rcParams['figure.dpi'] = 1080
pkdata = []
labels = []
lstyles = []
fg, ax = plt.subplots(3,3)
for pk in range(len(neonDict)):
px = pk // 3
py = pk % 3
ax[px,py].set_xlabel('Max Pixel')
ax[px,py].set_ylabel('')
ax[px,py].set_title(str(neonDict[pk]) + ' nm')
for cat in range(len(data)):
bp = ''
pkdata = [[],[],[],[],[],[],[],[],[]]
for acal in data[cat]:
for apeak in acal.peaks:
pkdata[apeak].append(acal.peaks[apeak][0])
for pk in range(9):
px = pk // 3
py = pk % 3
bp = coloredBoxPlot(ax[px,py], pkdata[pk], colors[cat], '#ffffff')
if len(data[cat]) > 0:
print(compareHexColors(colors[cat],getHex(*bp['boxes'][0].get_edgecolor())))
labels.append(prefix+' '+str(cat))
lstyles.append(bp['boxes'][0])
fg.legend(lstyles,labels)
fg.suptitle('Calibration Summary by '+prefix)
fg.tight_layout()
if savedir is not None:
plt.savefig(savedir + 'Boxplots.png')
plt.show()
mpl.rcParams['figure.figsize'] = csize
mpl.rcParams['figure.dpi'] = cdpi
return
class acal:
def __init__(self,center):
self.peaks = {}
for x in range(9):
self.peaks[x] = [10*x + (center) + (np.random.randint(10)-1)/2.0,0,0]
mockData = [[acal(x) for y in range(1000)] for x in range(6)]
#Some unused channels
mockData[2] = []
mockData[3] = []
mockData[4] = []
plotCalStats(mockData)
我有一个数据集,它是一个列表列表。
每个列表都是一个要绘制为箱线图的类别。
每个列表都有一个最多包含 9 个要绘制成子图的组件的列表。
下面我使用的函数是基于这个answer。我把它从我的工作中拉出来并添加了一些模拟数据。下面应该是一个最小的例子。
neonDict = {
0:0, 1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7, 8:8
}
import matplotlib as mpl
import matplotlib.pyplot as plt
def coloredBoxPlot(axis, data,edgeColor,fillColor):
bp = axis.boxplot(data,vert=False,patch_artist=True)
for element in ['boxes', 'whiskers', 'fliers', 'means', 'medians', 'caps']:
plt.setp(bp[element], color=edgeColor)
for patch in bp['boxes']:
patch.set(facecolor=fillColor)
return bp
def plotCalStats(data, prefix='Channel', savedir=None,colors=['#00597c','#a8005c','#00aeea','#007d50','#400080','#e07800'] ):
csize = mpl.rcParams['figure.figsize']
cdpi = mpl.rcParams['figure.dpi']
mpl.rcParams['figure.figsize'] = (12,8)
mpl.rcParams['figure.dpi'] = 1080
pkdata = []
labels = []
lstyles = []
fg, ax = plt.subplots(3,3)
for pk in range(len(neonDict)):
px = pk // 3
py = pk % 3
ax[px,py].set_xlabel('Max Pixel')
ax[px,py].set_ylabel('')
ax[px,py].set_title(str(neonDict[pk]) + ' nm')
pkdata.append([])
for cat in range(len(data)):
bp = ''
for acal in data[cat]:
for apeak in acal.peaks:
pkdata[apeak].append(acal.peaks[apeak][0])
for pk in range(9):
px = pk // 3
py = pk % 3
bp = coloredBoxPlot(ax[px,py], pkdata[pk], colors[cat], '#ffffff')
if len(data[cat]) > 0:
#print(colors[cat])
#print(bp['boxes'][0].get_edgecolor())
labels.append(prefix+' '+str(cat))
lstyles.append(bp['boxes'][0])
fg.legend(lstyles,labels)
fg.suptitle('Calibration Summary by '+prefix)
fg.tight_layout()
if savedir is not None:
plt.savefig(savedir + 'Boxplots.png')
plt.show()
mpl.rcParams['figure.figsize'] = csize
mpl.rcParams['figure.dpi'] = cdpi
return
class acal:
def __init__(self):
self.peaks = {}
for x in range(9):
self.peaks[x] = (np.random.randint(20*x,20*(x+1)),)
mockData = [[acal() for y in range(100)] for x in range(6)]
#Some unused channels
mockData[2] = []
mockData[3] = []
mockData[4] = []
plotCalStats(mockData)
所以问题是绘图颜色与图例不符。即使我将数据限制为仅在数据存在时添加标签(因此确保使用空数据集调用箱线图并且没有获得适当的 PathPatch 没有问题。
打印输出验证颜色是否正确存储在 PathPatch 中。 (我可以添加我的数字 -> 十六进制转换器)如果有疑问。
附件是输出。可以看到我得到了一个紫色的盒子,但传说中没有紫色。紫色是第 4 个类别,它是空的。
知道标签与实际样式不符的原因吗?非常感谢!
编辑: 解决关于 'confusing' 的问题。 我有六类数据,每一类都来自一个事件。每个事件有 9 个组成部分。我想比较所有事件,对于每个单独的组件,对于单个图上的每个类别,如下所示。
每个子图都是一个单独的组件,由每个分类(通道)的数据系列组成。
所以我提供的 link(就像我说的,改编自)展示了如何在一个轴上为 2 个数据集创建一个箱线图。我基本上对 9 轴上的 6 个数据集做了同样的事情,其中 3 个数据集是空的(但不必如此,我这样做是为了说明问题。如果我有所有 6 个数据集,怎么能你说颜色乱了?????)
关于阿尔法:
仅向 matplotlib 提供 RGB 数据时,alpha 总是 'ff'。如果我调用 get_edgecolors,它将 return 一个元组 (RGBA),其中 A = 1.0。 请参阅注释掉的打印语句。
编辑 2:
如果我将其限制为单个类别,则箱形图视图不会那么混乱。
不确定错误是如何出现的,但问题与在创建箱形图之前重新格式化数据有关。
通过在循环类别之前创建子图期间删除 pkdata.append([])
并添加:
pkdata = [[],[],[],[],[],[],[],[],[]]
在类别循环的每次迭代中修复了该问题。前者正在发送所有以前的频道数据...
输出现在更好了。附上完整的 sol。
可能,因为绘图使用来自 pkdata 的数据,空通道 (data[cat]) 绘制了以前的数据(来自 data[cat-1]),因为它仍然在 pkdata 中(实际上,所有以前的数据 [cat] ] 仍在 pkdata 中)然后被绘制。我只检查 data[cat] 以获取每个循环中的数据以添加到图例中。例如,图例是为通道 0、1、5 设置的。但是我们看到通道的数据:0 为 0,0+1 为 1,0+1 为 2,0+1 为 3,0+1 为4、0+1+5 为 5... 因此通道 4(紫色)有要绘制的数据,但未添加到图例中。给人的印象是 'misaligned' 传奇,而不是非传奇数据...
单通道数据实际上是所有6通道重叠,最后通道5颜色为橙色,重叠所有之前的数据,即数据所属的原始通道0数据并适当添加到图例中。
neonDict = {
0:0, 1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7, 8:8
}
import matplotlib as mpl
import matplotlib.pyplot as plt
def getHex(r,g,b,a=1.0):
colors = [int(r * 255 ),int(g * 255 ),int(b * 255 ),int(a * 255) ]
s = '#'
for x in range(4):
cs = hex(colors[x])
if len(cs) == 3:
cs = cs + '0'
s += cs.replace('0x','')
return s
def getRGB(colstr):
try:
a = ''
r = int(colstr[1:3],16) / 255
g = int(colstr[3:5],16) / 255
b = int(colstr[5:7],16) / 255
if len (colstr) == 7:
a = 1.0
else:
a = int(colstr[7:],16) / 255
return (r,g,b,a)
except Exception as e:
print(e)
raise e
return
def compareHexColors(col1,col2):
try:
## ASSUME #RBG or #RBGA
## If less than 7, append the ff for the colors
if len(col1) < 9:
col1 += 'ff'
if len(col2) < 9:
col2 += 'ff'
return col1.lower() == col2.lower()
except Exception as e:
raise e
return False
def coloredBoxPlot(axis, data,edgeColor,fillColor):
bp = axis.boxplot(data,vert=False,patch_artist=True)
for element in ['boxes', 'whiskers', 'fliers', 'means', 'medians', 'caps']:
plt.setp(bp[element], color=edgeColor)
for patch in bp['boxes']:
patch.set(facecolor=fillColor)
return bp
def plotCalStats(data, prefix='Channel', savedir=None,colors=['#00597c','#a8005c','#00aeea','#007d50','#400080','#e07800'] ):
csize = mpl.rcParams['figure.figsize']
cdpi = mpl.rcParams['figure.dpi']
mpl.rcParams['figure.figsize'] = (12,8)
mpl.rcParams['figure.dpi'] = 1080
pkdata = []
labels = []
lstyles = []
fg, ax = plt.subplots(3,3)
for pk in range(len(neonDict)):
px = pk // 3
py = pk % 3
ax[px,py].set_xlabel('Max Pixel')
ax[px,py].set_ylabel('')
ax[px,py].set_title(str(neonDict[pk]) + ' nm')
for cat in range(len(data)):
bp = ''
pkdata = [[],[],[],[],[],[],[],[],[]]
for acal in data[cat]:
for apeak in acal.peaks:
pkdata[apeak].append(acal.peaks[apeak][0])
for pk in range(9):
px = pk // 3
py = pk % 3
bp = coloredBoxPlot(ax[px,py], pkdata[pk], colors[cat], '#ffffff')
if len(data[cat]) > 0:
print(compareHexColors(colors[cat],getHex(*bp['boxes'][0].get_edgecolor())))
labels.append(prefix+' '+str(cat))
lstyles.append(bp['boxes'][0])
fg.legend(lstyles,labels)
fg.suptitle('Calibration Summary by '+prefix)
fg.tight_layout()
if savedir is not None:
plt.savefig(savedir + 'Boxplots.png')
plt.show()
mpl.rcParams['figure.figsize'] = csize
mpl.rcParams['figure.dpi'] = cdpi
return
class acal:
def __init__(self,center):
self.peaks = {}
for x in range(9):
self.peaks[x] = [10*x + (center) + (np.random.randint(10)-1)/2.0,0,0]
mockData = [[acal(x) for y in range(1000)] for x in range(6)]
#Some unused channels
mockData[2] = []
mockData[3] = []
mockData[4] = []
plotCalStats(mockData)