简体   繁体   English

Matplotlib - 对数图表 - 向图表添加额外文本

[英]Matplotlib - logarithmic chart - adding extra text to chart

I am trying to create a readable layout of a logarithmic chart, based on a pandas dataframe.我正在尝试基于 pandas dataframe 创建对数图表的可读布局。 I have the following code:我有以下代码:

fig, ax = plt.subplots(figsize=(15, 8))
ax.clear()
ax.barh(mydataframe['Labels'], mydataframe['Values']
plt.xscale('log')
for i, (label, value) in enumerate(zip(mydataframe['Labels'], mydataframe['Values'])):
            ax.text(value + 1, i, f'{value:,.0f}', size=8, ha='left', va='center')
            ax.text(value + 20, i, f'({int(round(extraValue1)):d} -- {extraValue2:,.2f}%)', size=8, ha='left', va='center')

图表布局

As you can see in the picture, my problem is the alignment of the second ax.text print on the chart.如图所示,我的问题是图表上第二个 ax.text 打印的 alignment。 The first line of ax.text aligns perfectly the values at the end of each bar. ax.text 的第一行完美地对齐每个条末尾的值。 But the second one, with the additional information I need to add, the alignment is messed up and overwrites the previous value, the more we go up to the bigger bars.但是第二个,随着我需要添加的附加信息,alignment 搞砸了并覆盖了以前的值,我们 go 越多到更大的条。 Does anyone know how I can fix this?有谁知道我该如何解决这个问题? I have a feeling it's related to the chart being set with logarithmic scale.我感觉这与使用对数刻度设置的图表有关。 The extravalue1 and 2 and also part of column of mydataframe, but for the simplicity of the code, I defined here the enumerate for only the main columns extravalue1 和 2 也是 mydataframe 列的一部分,但为了代码的简单性,我在这里定义了仅主要列的枚举

To have a nice distance between the bar and the text, it is better to use the exact value for the x of the text and start the text with one or two spaces.为了使条形图和文本之间有一个很好的距离,最好使用文本x的精确值,并以一两个空格开始文本。 So, using ax.text(value, i, " abc") instead of ax.text(value+20, i, "abc") .因此,使用ax.text(value, i, " abc")而不是ax.text(value+20, i, "abc") To have two texts, just concatenate them and place them in one go.要拥有两个文本,只需将它们连接起来并将它们放在一个 go 中。

If you want text with multiple colors, and have them placed together with a gap adapting to the length of the first text, offset boxes can be used.如果您想要包含多个 colors 的文本,并将它们放置在一起,并有一个适合第一个文本长度的间隙,可以使用偏移框。

To have more space for the text, you can increase the x-margins, eg ax.margins(x=0.2) .要为文本留出更多空间,您可以增加 x 边距,例如ax.margins(x=0.2) At the same time you can decrease the y-margins, for which the defaults are a bit too large here.同时,您可以减少 y 边距,这里的默认值有点太大。

import matplotlib.pyplot as plt
from matplotlib.colors import CSS4_COLORS
from matplotlib.offsetbox import AnchoredOffsetbox, TextArea, HPacker
import numpy as np

fig, ax = plt.subplots(figsize=(15, 8))
labels = ["".join(np.random.choice(list('abcdef'), 5)) for _ in range(20)]
values = np.sort(10 ** np.random.uniform(1, 5, 20))
ax.barh(labels, values, color=np.random.choice(list(CSS4_COLORS.keys()), 20))
ax.set_xscale('log')
for i, (label, value, extraValue1, extraValue2) in enumerate(
        zip(labels, values, *np.random.randint(100, 1000, (2, 20)))):
    xbox1 = TextArea(f'{value:,.0f}', textprops=dict(color="crimson", size=8, ha='left', va='baseline'))
    xbox2 = TextArea(f'({int(round(extraValue1)):d} -- {extraValue2:,.2f}%)',
                     textprops=dict(color="navy", size=8, ha='left', va='baseline'))
    xbox = HPacker(children=[xbox1, xbox2], align="baseline", pad=0, sep=5)
    anchored_xbox = AnchoredOffsetbox(loc="center left", child=xbox, pad=0.5, frameon=False, bbox_to_anchor=(value, i),
                                      borderpad=0, bbox_transform=ax.transData)
    ax.add_artist(anchored_xbox)
ax.margins(x=0.2, y=0.01)
plt.show()

示例图

Even for the classic (non-log) chart it was a bit messed up, but I fixed with an indent based on value (if it's three or four digits)即使对于经典(非对数)图表,它也有点混乱,但我用基于值的缩进(如果它是三位数或四位数)进行了修复

if value<1000:
            indent = 80
        else:
            indent = 120
ax.text(value + 20, i, f'({int(round(extraValue1)):d} -- {extraValue2:,.2f}%)', size=8, ha='left', va='center')

The problem remains with log chart日志图表仍然存在问题经典图表

Just add the text you want to plot as one string not to:只需将您想要的文本添加到 plot 作为一个字符串不要:

for i, (label, value) in enumerate(zip(mydataframe['Labels'], mydataframe['Values'])):
    text = f'{value:,.0f}\t({int(round(extraValue1)):d} -- {extraValue2:,.2f}%)'
    ax.text(value + 1, i, text, size=8, ha='left', va='center')

Or am I missing something why you do not want to do this?或者我错过了什么你不想这样做的原因?

Since your x-axis is logarithmic, you'd want to use a multiplicative offset for your text, instead of an additive one.由于您的 x 轴是对数的,因此您希望对文本使用乘法偏移量,而不是加法偏移量。 That is, try value * 1.2 instead of value + 20 :也就是说,尝试value * 1.2而不是value + 20

fig, ax = plt.subplots(figsize=(15, 8))
ax.clear()
ax.barh(mydataframe['Labels'], mydataframe['Values']
plt.xscale('log')
for i, (label, value) in enumerate(zip(mydataframe['Labels'], mydataframe['Values'])):
            ax.text(value * 1.05, i, f'{value:,.0f}', size=8, ha='left', va='center')
            ax.text(value * 1.2, i, f'({int(round(extraValue1)):d} -- {extraValue2:,.2f}%)', size=8, ha='left', va='center')

Please tune the 1.05 and 1.2 to fit your needs.请调整1.051.2以满足您的需求。 The formatting could look at bit nicer if you combined both texts into one, but that's just taste.如果您将两个文本合并为一个,格式可能会看起来更好,但这只是品味。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM