Я хочу экспортировать некоторые кольцевые участки, которые должны иметь одинаковое точное положение и размер (например, чтобы я мог вставить также круглое изображение и отцентрировать его, просто зная положение, в котором оно должно быть вставлено, благодаря стандартизированному размеру /должность). В качестве примера того, что было бы желательно, представьте, что если бы кто-то использовал обычную программу просмотра изображений и go для следующего изображения в группе, он увидел бы только изменение веса и цвета на графике и не увидел бы каких-либо изменений в позиция / размер.
Я попробовал это, настроив одинаковый размер в дюймах для всех фигур, а затем экспортировав их с одинаковым размером в пикселях; однако, matplotlib по-прежнему экспортирует графики с разными размерами, что, хотя и незначительно, может повлиять на плавный визуальный эффект, которого я хочу достичь. У меня был какой-то код, в который были добавлены некоторые метки и раскраски, но я решил упростить код, чтобы кому-то было проще помочь мне с этой проблемой. Я также попытался применить код, увиденный в этой статье (см. Попытку 3 в коде, который использует функции get_size и set_size ), но он все еще имеет проблема в том, что выходные графики немного отличаются друг от друга.
Вот код (особенно обратите внимание на строки, перед которыми стоит комментарий "попытки #"):
# coding=utf8
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.image import imread
from tempfile import NamedTemporaryFile
def get_size(fig, dpi=100):
with NamedTemporaryFile(suffix='.png') as f:
fig.savefig(f.name, bbox_inches='tight', dpi=dpi)
height, width, _channels = imread(f.name).shape
return width / dpi, height / dpi
def set_size(fig, size, dpi=100, eps=1e-2, give_up=2, min_size_px=10):
target_width, target_height = size
set_width, set_height = target_width, target_height # reasonable starting point
deltas = [] # how far we have
while True:
fig.set_size_inches([set_width, set_height])
actual_width, actual_height = get_size(fig, dpi=dpi)
set_width *= target_width / actual_width
set_height *= target_height / actual_height
deltas.append(abs(actual_width - target_width) + abs(actual_height - target_height))
if deltas[-1] < eps:
return True
if len(deltas) > give_up and sorted(deltas[-give_up:]) == deltas[-give_up:]:
return False
if set_width * dpi < min_size_px or set_height * dpi < min_size_px:
return False
d_acts_1 = {'activity': ['Activity 1', 'Activity 3', 'Activity 4', 'Activity 7', 'Activity 9'],
'weight': [0.2, 0.1, 0.4, 0.1, 0.2]}
d_acts_2 = {'activity': ['Activity 1', 'Activity 3', 'Activity 4', 'Activity 7', 'Activity 9'],
'weight': [0.1, 0.2, 0.1, 0.2, 0.4]}
d_acts_3 = {'activity': ['Activity 2', 'Activity 4', 'Activity 5', 'Activity 6', 'Activity 7'],
'weight': [0.2, 0.4, 0.1, 0.1, 0.2]}
d_acts_4 = {'activity': ['Activity 1', 'Activity 2', 'Activity 5', 'Activity 7', 'Activity 8'],
'weight': [0.1, 0.3, 0.4, 0.1, 0.1]}
df_acts_1 = pd.DataFrame(data=d_acts_1)
df_acts_2 = pd.DataFrame(data=d_acts_2)
df_acts_3 = pd.DataFrame(data=d_acts_3)
df_acts_4 = pd.DataFrame(data=d_acts_4)
df_groups_1 = pd.DataFrame(data={'group': ['Type 1', 'Type 2', 'Type 3'],
'weight': [0.3, 0.5, 0.2]})
df_groups_2 = pd.DataFrame(data={'group': ['Type 1', 'Type 2', 'Type 3'],
'weight': [0.1, 0.3, 0.6]})
df_groups_3 = pd.DataFrame(data={'group': ['Type 1', 'Type 2', 'Type 3'],
'weight': [0.7, 0.3, 0.0]})
df_groups_4 = pd.DataFrame(data={'group': ['Type 1', 'Type 2', 'Type 3'],
'weight': [0.4, 0.6, 0.0]})
df_grp_act = pd.DataFrame(data={'groups': [df_groups_1, df_groups_2, df_groups_3, df_groups_4],
'activities': [df_acts_1, df_acts_2, df_acts_3, df_acts_4]})
# create donut plots
i = 1
for _, row in df_grp_act.iterrows():
df_groups = row['groups']
df_activities = row['activities']
# create lists ignoring "0%" content, for outer and inner rings
outer_names = df_groups[df_groups['weight'] != 0].group.tolist()
outer_weights = df_groups[df_groups['weight'] != 0].weight.tolist()
inner_names = df_activities[df_activities['weight'] != 0].activity.tolist()
inner_weights = df_activities[df_activities['weight'] != 0].weight.tolist()
# create blank figure and add donut plots
fig, ax = plt.subplots()
# fig.set_size_inches(8, 8)
# pie_outer, _ = ax.pie(outer_weights, radius=1.3, labels=outer_names)
# inner_inner_labels = list(map(lambda x: str(round(x, 2) * 100)[:-2] + '%', inner_weights))
# pie_inner, texts = ax.pie(inner_weights, radius=1.3 - 0.3, labels=inner_inner_labels, labeldistance=0.7)
pie_outer, _ = ax.pie(outer_weights, radius=1.3)
pie_inner, _ = ax.pie(inner_weights, radius=1.3 - 0.3)
plt.setp(pie_outer, width=0.3, edgecolor='white')
plt.setp(pie_inner, width=0.4, edgecolor='white')
# attempt 1
ax.axis('equal') # equalizes axis units
plt.savefig(f"result-infographics/plots/plot.{i}.png")
# # attempt 2
# ax.axis('equal') # equalizes axis units
# my_dpi = 96
# fig.set_size_inches(900 / my_dpi, 800 / my_dpi)
# # plt.show()
# plt.savefig(f"result-infographics/plots/plot.{i}.png", dpi=my_dpi)
# # attempt 3
# set_size(fig, (8, 8))
# plt.savefig(f"result-infographics/plots/plot.{i}.png", bbox_inches='tight')
# print(imread(f"result-infographics/plots/plot.{i}.png").shape) # outputs (250, 500, 4)
i += 1
Примеры графиков, которые представьте эту проблему: Сюжет 1 Сюжет 2
Я бы хотел решение, которое использует matplotlib, так как я обнаружил, что в общем случае просто сделать с ним кольцевые заговоры , но любая альтернатива, с помощью которой я мог бы получить эту работу, также была бы оценена.