通过Python和MatPlotLib库使用笛卡尔坐标的有序对标注图

rbl8hiat  于 2023-03-03  发布在  Python
关注(0)|答案(1)|浏览(134)

寻找一个解决方案,以正确地注解一个子图与一对有序的笛卡尔坐标。
我的图是一个总产品数量的条形图和一个给定产品平均价格的线形图。要获得更多参考信息,请参阅本文末尾的图:https://medium.com/swlh/product-sales-analysis-using-python-863b29026957
请注意,我有两个垂直轴:

  • y1 =给定产品的总量
  • y2 =给定产品的平均价格
  • y1和y2共享产品类别的x轴

我的目标不是绘制标签“(x,y)",而是绘制(y1,y2)的标签,即“(qty,price)"。
我遇到的当前错误是变量label中的列表元素没有被识别为“subscriptableobjects”。我的印象是解决方案是将列表中的每个元素转换为字符串,但我并不肯定。
df =
| 产品|数量|价格|
| - ------|- ------|- ------|
| 产品1|十个|一百元|
| 产品2|十五|二百元|
| 产品3|二十个|一百五十元|
| 产品2|三十|二百元|
| 产品3|五十|一百五十元|

尝试

quantity = df.groupby("Products")["Quantity"].sum()
price = df.groupby("Products")["Price"].mean()

fig, ax1 = plt.subplots()
ax2 = ax1.twinx()

ax1.bar(Products, quantity, color='.8', alpha =.8)
ax2.plot(Products, price, 'bo-')

ax1.set_xlabel('', fontweight='bold')
ax1.set_ylabel('Quantity', color = 'k', fontweight='bold')
ax2.set_ylabel('Price $', color = 'b', fontweight='bold')
ax1.set_xticklabels(Products, rotation=45, size = 8)

y1 = [i for i in quantity]
y2 = [j for j in price]

label = []
for x, y in zip(y1,y2):
    label.append(f"({x:.2f},{y:.2f})")

for i, label in enumerate(labels):
    plt.annotate(label, xy=(x[i], y[i]), xytext=(5, 5),
    textcoords='offset points', ha='left', va='bottom')
plt.show()

故障区域

#can't find a method to convert my list elements from float to string values *inline* with label.append()
label = []
for x, y in zip(y1,y2):
    label.append(f"({x:.2f},{y:.2f})")

我觉得我正在寻找一个类似的解决方案:

  1. https://www.tutorialspoint.com/how-to-annotate-several-points-with-one-text-in-matplotlib
  2. https://queirozf.com/entries/add-labels-and-text-to-matplotlib-plots-annotation-examples
pdkcd3nj

pdkcd3nj1#

代码中存在一些误解:

  • 当使用ax1ax2时,建议在任何地方都使用matplotlib的object-oriented interfaceplt.annotate(...)将在“当前轴”上绘图,而ax1.annotate(...)将在ax1上绘图。
  • 在最新的matplotlib版本中,要在ax1上绘图,x坐标可以作为字符串(产品的名称)给出,y坐标作为数值量给出。
  • 在调用注解的同时创建要打印的标签可以使代码更易于阅读。
  • 要创建循环,在Python中强烈建议尽量避免使用enumerate(...)和索引,使用zip直接获取列表元素会使循环更清晰。
  • 要旋转刻度,ax.tick_params(...)将保持现有标签不变。
  • ax1.margins(y=...)可以为标签腾出更多的可用空间。
import matplotlib.pyplot as plt
import pandas as pd

df = pd.DataFrame({'Products': ['Product1', 'Product2', 'Product3', 'Product2', 'Product3'],
                   'Quantity': [10, 15, 20, 30, 50],
                   'Price': [100, 200, 150, 200, 150]})
quantity = df.groupby("Products")["Quantity"].sum()
price = df.groupby("Products")["Price"].mean()
Products = quantity.index

fig, ax1 = plt.subplots()
ax2 = ax1.twinx()

ax1.bar(Products, quantity, color='.8', alpha=.8)
ax2.plot(Products, price, 'bo-')

ax1.set_xlabel('', fontweight='bold')
ax1.set_ylabel('Quantity', color='k', fontweight='bold')
ax2.set_ylabel('Price $', color='b', fontweight='bold')
ax1.tick_params(axis='x', rotation=45, labelsize=8)

for prod, quant, prc in zip(Products, quantity, price):
    ax1.annotate(f'{quant:.2f}, {prc:.2f}', xy=(prod, quant), xytext=(0, 5),
                 textcoords='offset points', ha='center', va='bottom')
ax1.margins(y=0.2)  # more space on top of the bars
plt.tight_layout()
plt.show()

相关问题