我正在尝试通过使色调影响颜色以及标记的另一个几何特征,使黑白绘图以及对有色盲的人来说更易于阅读。
import seaborn as sns
import matplotlib.pyplot as plt
sns.set(style="whitegrid")
tips = sns.load_dataset("tips")
fig, ax = plt.subplots(1,1)
ax = sns.swarmplot(x="day", y="total_bill", hue="sex",data=tips,size=8,ax=ax)
plt.show()
答案 0 :(得分:0)
实际上,我前段时间曾想到过同样的问题。我没有提出最好的解决方案,但是我有一个可以正常工作的技巧。不幸的是,如果使用dodge=True
,实现起来会容易得多。
这个想法是收集PathCollections
创建的swarmplot
对象。如果是dodge=True
,那么您将获得N_cat*N_hues+N_hues
个集合(N_hues附加元素用于创建图例)。您可以简单地遍历该列表。由于我们希望所有色调都相同,因此我们使用N_hues步幅来获取与每种色调相对应的所有集合。之后,您可以将该集合的paths
更新为您选择的任何Path
对象。请参阅the documentation for Path
,了解如何创建路径。
为简化起见,我在动手之前创建了一些虚拟散点图,以获得可以使用的预制Paths
。当然,任何Path
都应该可以使用。
import seaborn as sns
import matplotlib.pyplot as plt
sns.set(style="whitegrid")
tips = sns.load_dataset("tips")
fig, ax = plt.subplots(1,1)
# dummy plots, just to get the Path objects
a = ax.scatter([1,2],[3,4], marker='s')
b = ax.scatter([1,2],[3,4], marker='^')
square_mk, = a.get_paths()
triangle_up_mk, = b.get_paths()
a.remove()
b.remove()
ax = sns.swarmplot(x="day", y="total_bill", hue="sex",data=tips,size=8,ax=ax, dodge=True)
N_hues = len(pd.unique(tips.sex))
c = ax.collections
for a in c[::N_hues]:
a.set_paths([triangle_up_mk])
for a in c[1::N_hues]:
a.set_paths([square_mk])
#update legend
ax.legend(c[-2:],pd.unique(tips.sex))
plt.show()
更新一种可与dodge=False
“配合使用”的解决方案。
如果使用dodge=False
,则将获得N + 2个集合,每个类别一个,图例+2。问题在于这些集合中所有不同的标记颜色都被弄乱了。
一种可能但丑陋的解决方案是遍历集合的每个元素,并根据每个元素的颜色创建Path
对象的数组。
import seaborn as sns
import matplotlib.pyplot as plt
sns.set(style="whitegrid")
tips = sns.load_dataset("tips")
fig, ax = plt.subplots(1,1)
ax = sns.swarmplot(x="day", y="total_bill", hue="sex",data=tips,size=8,ax=ax, dodge=False)
collections = ax.collections
unique_colors = np.unique(collections[0].get_facecolors(), axis=0)
markers = [triangle_up_mk, square_mk] # this array must be at least as large as the number of unique colors
for collection in collections:
paths = []
for current_color in collection.get_facecolors():
for possible_marker,possible_color in zip(markers, unique_colors):
if np.array_equal(current_color,possible_color):
paths.append(possible_marker)
break
collection.set_paths(paths)
#update legend
ax.legend(collections[-2:],pd.unique(tips.sex))
plt.show()
答案 1 :(得分:0)
以下内容将提供一个技巧,使您可以轻松地实现所需的用于形变的标记(或更一般而言,是任何类别的散布图)。它可以按原样使用,只需将其复制到现有绘图脚本的顶部即可。
该想法是将散射点的颜色与标记关联起来。例如。任何分散点都会自动从指定列表中获取标记。因此,这仅适用于具有不同颜色的图。
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
############## Begin hack ##############
class CM():
def __init__(self, markers=["o"]):
self.marker = np.array(markers)
self.colors = []
def get_markers_for_colors(self, c):
for _co in c:
if not any((_co == x).all() for x in self.colors):
self.colors.append(_co)
ind = np.array([np.where((self.colors == row).all(axis=1)) \
for row in c]).flatten()
return self.marker[ind % len(self.marker)]
def get_legend_handles(self, **kwargs):
return [plt.Line2D([0],[0], ls="none", marker=m, color=c, mec="none", **kwargs) \
for m,c in zip(self.marker, self.colors)]
from matplotlib.axes._axes import Axes
import matplotlib.markers as mmarkers
cm = CM(plt.Line2D.filled_markers)
old_scatter = Axes.scatter
def new_scatter(self, *args, **kwargs):
sc = old_scatter(self, *args, **kwargs)
c = kwargs.get("c", None)
if isinstance(c, np.ndarray):
m = cm.get_markers_for_colors(c)
paths = []
for _m in m:
marker_obj = mmarkers.MarkerStyle(_m)
paths.append(marker_obj.get_path().transformed(
marker_obj.get_transform()))
sc.set_paths(paths)
return sc
Axes.scatter = new_scatter
############## End hack. ##############
# Copy and past to your file ##########
## Code ###
sns.set(style="whitegrid")
tips = sns.load_dataset("tips")
fig, ax = plt.subplots(1,1)
## Optionally specify own markers:
#cm.marker = np.array(["^", "s"])
ax = sns.swarmplot(x="day", y="total_bill", hue="sex",data=tips,size=8,ax=ax)
## Optionally adjust legend:
_,l = ax.get_legend_handles_labels()
ax.legend(cm.get_legend_handles(markersize=8),l)
plt.show()
答案 2 :(得分:0)
感谢@ImportanceOfBeingErnest提供解决方案。我试图编辑他/她的解决方案以解决一些小问题,但最后他/她建议我发表自己的答案。
此解决方案与他/她的解决方案相同,但是当未指定标记数组时,它不会改变正常散布的行为。它也更易于应用,并且可以修复图例失去标题的错误。
下图是由以下代码产生的:
import seaborn as sns
import matplotlib.pyplot as plt
############## Begin hack ##############
from matplotlib.axes._axes import Axes
from matplotlib.markers import MarkerStyle
from seaborn import color_palette
from numpy import ndarray
def GetColor2Marker(markers):
palette = color_palette()
mkcolors = [(palette[i]) for i in range(len(markers))]
return dict(zip(mkcolors,markers))
def fixlegend(ax,markers,markersize=8,**kwargs):
# Fix Legend
legtitle = ax.get_legend().get_title().get_text()
_,l = ax.get_legend_handles_labels()
palette = color_palette()
mkcolors = [(palette[i]) for i in range(len(markers))]
newHandles = [plt.Line2D([0],[0], ls="none", marker=m, color=c, mec="none", markersize=markersize,**kwargs) \
for m,c in zip(markers, mkcolors)]
ax.legend(newHandles,l)
leg = ax.get_legend()
leg.set_title(legtitle)
old_scatter = Axes.scatter
def new_scatter(self, *args, **kwargs):
colors = kwargs.get("c", None)
co2mk = kwargs.pop("co2mk",None)
FinalCollection = old_scatter(self, *args, **kwargs)
if co2mk is not None and isinstance(colors, ndarray):
Color2Marker = GetColor2Marker(co2mk)
paths=[]
for col in colors:
mk=Color2Marker[tuple(col)]
marker_obj = MarkerStyle(mk)
paths.append(marker_obj.get_path().transformed(marker_obj.get_transform()))
FinalCollection.set_paths(paths)
return FinalCollection
Axes.scatter = new_scatter
############## End hack. ##############
# Example Test
sns.set(style="whitegrid")
tips = sns.load_dataset("tips")
# To test robustness
tips.loc[(tips['sex']=="Male") & (tips['day']=="Fri"),'sex']='Female'
tips.loc[(tips['sex']=="Female") & (tips['day']=="Sat"),'sex']='Male'
Markers = ["o","P"]
fig, axs = plt.subplots(1,2,figsize=(14,5))
axs[0] = sns.swarmplot(x="day", y="total_bill", hue="sex",data=tips,size=8,ax=axs[0])
axs[0].set_title("Original")
axs[1] = sns.swarmplot(x="day", y="total_bill", hue="sex",data=tips,size=8,ax=axs[1],co2mk=Markers)
axs[1].set_title("Hacked")
fixlegend(axs[1],Markers)
plt.show()