首页 > 解决方案 > 如何在不更改 yticks 的情况下使用 Matplotlib 实现非重叠文本

问题描述

我正在使用下面的代码实现下图。

数据:

df={'continent': {0: 'BJP', 1: 'INC', 2: 'BSP'},
 '1952': {0: 10650, 1: 30047, 2: 36828},
 '1957': {0: 42369, 1: 4482, 2: 4069}}

代码:

import matplotlib.lines as mlines
# Import Data
#df = pd.read_csv("C:\\Daman\\abccc.csv")

left_label = [str(c) + ', '+ str(round(y)) for c, y in zip(df.continent, df['1952'])]
right_label = [str(c) + ', '+ str(round(y)) for c, y in zip(df.continent, df['1957'])]
klass = ['red' if (y1-y2) < 0 else 'green' for y1, y2 in zip(df['1952'], df['1957'])]

# draw line
# https://stackoverflow.com/questions/36470343/how-to-draw-a-line-with-matplotlib/36479941
def newline(p1, p2, color='black'):
    ax = plt.gca()
    l = mlines.Line2D([p1[0],p2[0]], [p1[1],p2[1]], color='red' if p1[1]-p2[1] > 0 else 'green', marker='o', markersize=6)
    ax.add_line(l)
    return l

fig, ax = plt.subplots(1,1,figsize=(7,7), dpi= 80)

# Vertical Lines
ax.vlines(x=1, ymin=3500, ymax=45000, color='black', alpha=0.7, linewidth=1, linestyles='dotted')
ax.vlines(x=3, ymin=3500, ymax=45000, color='black', alpha=0.7, linewidth=1, linestyles='dotted')

# Points
ax.scatter(y=df['1952'], x=np.repeat(1, df.shape[0]), s=10, color='black', alpha=0.7)
ax.scatter(y=df['1957'], x=np.repeat(3, df.shape[0]), s=10, color='black', alpha=0.7)

# Line Segmentsand Annotation
for p1, p2, c in zip(df['1952'], df['1957'], df['continent']):
    newline([1,p1], [3,p2])
    ax.text(1-0.05, p1, c + ', ' + str(round(p1)), horizontalalignment='right', verticalalignment='center', fontdict={'size':14})
    ax.text(3+0.05, p2, c + ', ' + str(round(p2)), horizontalalignment='left', verticalalignment='center', fontdict={'size':14})

# 'Before' and 'After' Annotations
ax.text(1-0.05, 23000, 'BEFORE', horizontalalignment='right', verticalalignment='center', fontdict={'size':18, 'weight':700})
ax.text(3+0.05, 23000, 'AFTER', horizontalalignment='left', verticalalignment='center', fontdict={'size':18, 'weight':700})

# Decoration
ax.set_title("Vote Swing", fontdict={'size':22})
ax.set(xlim=(0,4), ylim=(0,14000), ylabel='Votes')
ax.set_xticks([1,3])
ax.set_xticklabels(["2012", "2017"])
plt.yticks(np.arange(3500, 45000, 2000), fontsize=12)

# Lighten borders
plt.gca().spines["top"].set_alpha(.0)
plt.gca().spines["bottom"].set_alpha(.0)
plt.gca().spines["right"].set_alpha(.0)
plt.gca().spines["left"].set_alpha(.0)
plt.show()

在此处输入图像描述

我的问题是:

  1. 如何删除这种重叠的文本,因为它们不可读。

在此处输入图像描述

  1. 如何在线条上方放置百分比(摆动)。
df2={'continent': {0: 'BJP', 1: 'INC', 2: 'BSP'},
 'Swing': {0: 31.9, 1: 9.9, 2: -42.5}}

在此处输入图像描述

如下图所示: 在此处输入图像描述

标签: pythonmatplotlib

解决方案


您可以使用adjust_text。尝试使用参数来获得适合您的外观:

import matplotlib.lines as mlines
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from adjustText import adjust_text

df=pd.DataFrame({'continent': {0: 'BJP', 1: 'INC', 2: 'BSP'},
 '1952': {0: 10650, 1: 30047, 2: 36828},
 '1957': {0: 42369, 1: 4482, 2: 4069}})

 # Import Data
#df = pd.read_csv("C:\\Daman\\abccc.csv")

left_label = [str(c) + ', '+ str(round(y)) for c, y in zip(df.continent, df['1952'])]
right_label = [str(c) + ', '+ str(round(y)) for c, y in zip(df.continent, df['1957'])]
klass = ['red' if (y1-y2) < 0 else 'green' for y1, y2 in zip(df['1952'], df['1957'])]

# draw line
def newline(p1, p2, color='black'):
    ax = plt.gca()
    l = mlines.Line2D([p1[0],p2[0]], [p1[1],p2[1]], color='red' if p1[1]-p2[1] > 0 else 'green', marker='o', markersize=6)
    ax.add_line(l)
    return l

fig, ax = plt.subplots(1,1,figsize=(7,7), dpi= 80)

# Vertical Lines
ax.vlines(x=1, ymin=3500, ymax=45000, color='black', alpha=0.7, linewidth=1, linestyles='dotted')
ax.vlines(x=3, ymin=3500, ymax=45000, color='black', alpha=0.7, linewidth=1, linestyles='dotted')

# Points
ax.scatter(y=df['1952'], x=np.repeat(1, df.shape[0]), s=10, color='black', alpha=0.7)
ax.scatter(y=df['1957'], x=np.repeat(3, df.shape[0]), s=10, color='black', alpha=0.7)

# Line Segmentsand Annotation <------------- This is where you use adjust_text
texts = []
swing_texts = []
for p1, p2, c, s in zip(df['1952'], df['1957'], df['continent'], df['Swing']):
    newline([1,p1], [3,p2])
    texts.append(plt.text(1-0.05, p1, c + ', ' + str(round(p1))))
    texts.append(plt.text(3+0.05, p2, c + ', ' + str(round(p2))))
    swing_texts.append(plt.text(2, 0.5*(p1 +p2), s))

adjust_text(texts)
adjust_text(swing_texts, arrowprops=dict(arrowstyle="->", color='r', lw=0.5))

# 'Before' and 'After' Annotations
ax.text(1-0.05, 23000, 'BEFORE', horizontalalignment='right', verticalalignment='center', fontdict={'size':18, 'weight':700})
ax.text(3+0.05, 23000, 'AFTER', horizontalalignment='left', verticalalignment='center', fontdict={'size':18, 'weight':700})

# Decoration
ax.set_title("Vote Swing", fontdict={'size':22})
ax.set(xlim=(0,4), ylim=(0,14000), ylabel='Votes')
ax.set_xticks([1,3])
ax.set_xticklabels(["2012", "2017"])
plt.yticks(np.arange(3500, 45000, 2000), fontsize=12)

# Lighten borders
plt.gca().spines["top"].set_alpha(.0)
plt.gca().spines["bottom"].set_alpha(.0)
plt.gca().spines["right"].set_alpha(.0)
plt.gca().spines["left"].set_alpha(.0)

plt.show()

结果:
在此处输入图像描述


推荐阅读