使用散点图自定义图例

时间:2019-01-31 17:44:35

标签: pandas matplotlib seaborn

我很难自定义散布图的图例。这是快照:

Fun with MatPlotLib

这是一个代码示例:

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
sns.set()

my_df = pd.DataFrame([[5, 3, 1], [2, 1, 2], [3, 4, 1], [1, 2, 1]],
                     columns=["DUMMY_CT", "FOO_CT", "CI_CT"])

g = sns.scatterplot("DUMMY_CT", "FOO_CT", data=my_df, size="CI_CT")
g.set_title("Number of Baz", weight="bold")
g.set_xlabel("Dummy count")
g.set_ylabel("Foo count")
g.get_legend().set_title("Baz count")

此外,如果有帮助,我可以在Jupyter实验室的笔记本中使用Python 3。

红色的问题

首先,我想隐藏CI_CT变量的名称(图片上以红色显示)。在探究了整个下午的全部文档之后,我发现了get_legend_handlers_label方法(请参阅here),该方法产生以下结果:

>>> g.get_legend_handles_labels()
([<matplotlib.collections.PathCollection at 0xfaaba4a8>,
  <matplotlib.collections.PathCollection at 0xfaa3ff28>,
  <matplotlib.collections.PathCollection at 0xfaa3f6a0>,
  <matplotlib.collections.PathCollection at 0xfaa3fe48>],
  ['CI_CT', '0', '1', '2'])

在哪里可以找到我亲爱的CI_CT字符串。但是,我无法更改此名称或将其完全隐藏。我发现了一种肮脏的方式,该方式主要是没有有效地使用作为data参数传递的数据帧。这是scatterplot调用:

g = sns.scatterplot("DUMMY_CT", "FOO_CT", data=my_df, size=my_df["CI_CT"].values)

结果在这里:

First issue solved in a dirty way

它可以工作,但是有没有 cleaner 方法来实现这一目标?

绿色问题

在此图例中显示0级别是不正确的,因为CI_CT的{​​{1}}列中没有零值。因此,这对于读者可能会产生误导,他们可能会认为较小的点代表0或1的值。我希望设置一种已定义的比例尺,以这种方式可以针对x和y轴进行设置。但是,我无法实现。有想法吗?

TL; DR:一个可以解决所有问题的更广泛的问题

那些冒险使我想知道是否有一种方法可以处理您可以使用my_dfhue参数以清晰的x轴和x轴方式传递给散点图的数据。真的有可能吗?

请原谅我的英语,如果问题过于广泛或标签不正确,请告诉我。

2 个答案:

答案 0 :(得分:1)

通过指定legend="full"解决了“绿色问题”,即图例条目多于大小条目。

g = sns.scatterplot(..., legend="full")

“红色问题”更为棘手。这里的问题是,Seaborn滥用正常的图例标签作为图例的标题。确实可以选择直接提供值而不是列名,以防止seaborn使用该列名。

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
sns.set()

my_df = pd.DataFrame([[5, 3, 1], [2, 1, 2], [3, 4, 1], [1, 2, 1]],
                     columns=["DUMMY_CT", "FOO_CT", "CI_CT"])

g = sns.scatterplot("DUMMY_CT", "FOO_CT", data=my_df, size=my_df["CI_CT"].values, legend="full")
g.set_title("Number of Baz", weight="bold")
g.set_xlabel("Dummy count")
g.set_ylabel("Foo count")
g.get_legend().set_title("Baz count")

plt.show()

enter image description here

如果您真的必须使用列名本身,那么一个棘手的解决方案是爬入图例并删除不需要的标签。

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
sns.set()

my_df = pd.DataFrame([[5, 3, 1], [2, 1, 2], [3, 4, 1], [1, 2, 1]],
                     columns=["DUMMY_CT", "FOO_CT", "CI_CT"])

g = sns.scatterplot("DUMMY_CT", "FOO_CT", data=my_df, size="CI_CT", legend="full")
g.set_title("Number of Baz", weight="bold")
g.set_xlabel("Dummy count")
g.set_ylabel("Foo count")
g.get_legend().set_title("Baz count")

#Hack to remove the first legend entry (which is the undesired title)
vpacker = g.get_legend()._legend_handle_box.get_children()[0]
vpacker._children = vpacker.get_children()[1:]

plt.show()

答案 1 :(得分:0)

我终于设法获得了想要的结果,但是采用了丑陋的方式。这对某人可能有用,但我不建议这样做。

将比例尺固定在图例中的解决方案包括将所有CI_CT列值移至负值(以保持标记大小的顺序和一致性)。然后,图例中显示的值将根据先前的数据更改(来自here的启发)进行校正。

但是,我没有找到一种更好的方法来使图例中的“ CI_CT”文本消失,而不会留下巨大的空白。

这是代码示例和结果。

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
sns.set()

my_df = pd.DataFrame([[5, 3, 1], [2, 1, 2], [3, 4, 1], [1, 2, 1]], columns=["DUMMY_CT", "FOO_CT", "CI_CT"])

# Substracting the maximal value of CI_CT for each value
max_val = my_df["CI_CT"].agg("max")
my_df["CI_CT"] = my_df.apply(lambda x : x["CI_CT"] - max_val, axis=1)

# scatterplot declaration
g = sns.scatterplot("DUMMY_CT", "FOO_CT", data=my_df, size=my_df["CI_CT"].values)
g.set_title("Number of Baz", weight="bold")
g.set_xlabel("Dummy count")
g.set_ylabel("Foo count")
g.get_legend().set_title("Baz count")

# Correcting legend values
l = g.legend_
for t in l.texts :
    t.set_text(int(t.get_text()) + max_val)

# Restoring the DF
my_df["CI_CT"] = my_df.apply(lambda x : x["CI_CT"] + max_val, axis=1)

Fancy yet badly produced scatterplot

我仍在寻找实现这一目标的更好方法。