Есть ли способ экспортировать множество круговых / кольцевых диаграмм с одинаковым размером и положением? (Matplotlib) - PullRequest
1 голос
/ 29 февраля 2020

Я хочу экспортировать некоторые кольцевые участки, которые должны иметь одинаковое точное положение и размер (например, чтобы я мог вставить также круглое изображение и отцентрировать его, просто зная положение, в котором оно должно быть вставлено, благодаря стандартизированному размеру /должность). В качестве примера того, что было бы желательно, представьте, что если бы кто-то использовал обычную программу просмотра изображений и 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, так как я обнаружил, что в общем случае просто сделать с ним кольцевые заговоры , но любая альтернатива, с помощью которой я мог бы получить эту работу, также была бы оценена.

1 Ответ

0 голосов
/ 29 февраля 2020

Ключ здесь состоит в том, чтобы установить одинаковые пределы оси для всех графиков. Например,

ax.set(xlim=(-1.45, 1.45), ylim=(-1.45, 1.45))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...