python, matplotlib, построение диаграмм
Понадобилось мне отрисовывать вот такие графики для блога, поэтому написал скрипт на python
Настройка окружения
Создаем папку для приложения, например gii_plot_creator
Инициализируем git репозитории
gii_plot_creator> git init
Добавляем файл для гита, в котором пропишем исключения, т.е. те данные которые не должны попадать в репозитории:
gii_plot_creator/.gitignore
venv
Создаем виртуальное окружение python.
Этим мы создадим папку venv
в папке проекта, где будет лежать python и библиотеки проекта
gii_plot_creator> python -m venv venv
Активируем python окружение
windows
gii_plot_creator> venv/Scripts/activate (venv) gii_plot_creator>
linux
gii_plot_creator> source (venv) gii_plot_creator>
Пишем файл зависимостей
Данный файл содержит зависимости нашего приложения, т.е. список тех библиотек, от которых зависит наш проект
requirements.txt
matplotlib
Установка зависимостей
(venv) gii_plot_creator> pip install -r requirements.txt
Файл данных
Для отображения придумаем структуру данных и запишем данные в файл, чтобы в будущем менять данные только в файле данных
gii_plot_creator/data.json
[ { "title": "AIDA64", "x_label": "MB/sec", "legends": [ "Чтение из памяти", "Запись в память", "Копирование в памяти" ], "scores": [ ["Core-i3 2100", [19530, 20094, 19267]], ["Core-i3 2100\nGT710", [19454, 20145, 18861]], ["Core-i3 2100\nGT1030", [19377, 20266, 19069]], ["Ryzen 5 2600\nGT710", [16364, 15975, 15822]], ["Ryzen 5 2600\nGT1030", [16700, 15990, 15806]] ] } ]
- title - заголовок графика
- x_label - подпись для оси х
- legends - описание показателей
- scores - значения показателей
Разработка скрипта
Напишем файл настроек
gii_plot_creator/settings.py
""" конфигурация и константы приложения """ # модуль для работы с путями import os # папка приложения, gii_plot_creator BASE_PATH = os.path.dirname(__file__) # путь к файлу с данными DATA_PATH = os.path.join(BASE_PATH, 'data.json')
Напишем файл, в котором придумаем исключения, которые могут возникнуть в нашем приложении
gii_plot_creator/exceptions.py
""" исключения, выбрасываемые приложением """ class AppException(Exception): """ базовый класс для исключений приложения """ message = None def __str__(self): return self.message class DataFileDoesNotExists(AppException): """ файл данных не существует """ message = 'Файла данных не существует' class DataFileStructureError(AppException): """ ошибка в структуре файла данных """ def __init__(self, err): self.message = f'Файл данных имеет не корректный формат ({err})'
Основной файл скрипта
gii_plot_creator/app.py
""" приложения для рисования графиков бенчмарков """ # модуль для работы с json данными import json # модуль для работы с путями import os # импортируем модуль для построения графиков import matplotlib.pyplot as plt # импортируем модуль исключении приложения import exceptions # импортируем модуль настроек приложения import settings def get_data(data_path): """ функция возвращает данные для построения графиков :param data_path: путь до файла с данными :type data_path: str :rtype: dict or list """ # проверяем, существует ли вообще путь до файла конфигурации # т.е. есть ли например у нас файл data.json в папке с приложением if not os.path.exists(data_path): # файла не существует, тогда выбрасываем понятное исключение # т.е. исключение определенного типа raise exceptions.DataFileDoesNotExists() # если дошли сюда, значит файл существует # будем пытаться его прочитать и преобразовать данные в структуру данных python # т.е. json строку преобразовываем в словарь или список try: with open(data_path, encoding='utf-8') as f: data = json.load(f) except Exception as err: # перехватываем все исключения # если не смогли преобразовать, то выбрасываем понятное исключение raise exceptions.DataFileStructureError(err) # файл есть, преобразовали структуру, возвращаем данные return data def create_plot(data): """ возвращает объекты управления с холстом :param data: данные для отрисовки :type data: dict :rtype: tuple """ # составляет список данных для оси Х x_pos_ticks = [score[0] for score in data['scores']] x_pos = list(range(len(x_pos_ticks))) # собираем список сведений по показателям для оси у y_poss = [ { 'legend': legend, 'y_pos': [], 'rects': [] } for legend in (data.get('legends') or (None,)) ] for score in data['scores']: for index, score_y in enumerate(score[1]): y_poss[index]['y_pos'].append(score_y) # получаем объекты для отображения fig, ax = plt.subplots() # далее определяем высоту для линии графика, в зависимости от количества показателей width = (1./len(y_poss))-(0.01*len(y_poss)) # минимальное значение показателя miny = None #максимальное значение показателя maxy = None for index, y_pos_info in enumerate(y_poss): legend = y_pos_info['legend'] y_pos = y_pos_info['y_pos'] y_pos_info['rects'].extend( plt.barh( [x - (width * index) for x in x_pos], y_pos, width, label=legend ) ) if miny is None: miny = min(y_pos) maxy = max(y_pos) else: miny = min((miny, min(y_pos))) maxy = max((maxy, max(y_pos))) # вычисляем дельту, она нужна будет для вычисления минимального и максимального значения для оси графика delta = (maxy - miny) / 100 miny -= delta maxy += delta*13 # для каждой линии показателя отрисовываем числовое значение for y_pos_info in y_poss: y_pos = y_pos_info['y_pos'] rects = y_pos_info['rects'] for rect, value in zip(rects, y_pos): ax.annotate( value, xy=(rect.get_width(), rect.get_y() - (-rect.get_height()/2)), xytext=(5, -4), textcoords='offset points', ) # задаем минимальное и максимальное значение для оси Х ax.set_xlim(miny if miny > 0 else 0, maxy) # задаем значения для оси у ax.set_yticks(x_pos) # задаем отображаемые величины по оси у ax.set_yticklabels(x_pos_ticks) # устанавливаем заголовок ax.set_title(data['title']) # отрисовываем легенду, если легенда есть if data.get('legends'): ax.legend() # отрисовываем подпись для оси х if 'x_label' in data: ax.set_xlabel(data['x_label']) def main(): """ основная функция скрипта """ # получаем данные для отрисовки draw_data = get_data(settings.DATA_PATH) # рисуем каждый график в отдельный файл for data in draw_data: # отрисовываем данные create_plot(data) # центруем данные plt.tight_layout() # сохраняем в файл plt.savefig('{0}.png'.format(data['title'])) # отображаем при необходимости plt.show() # перехватываем все исключения приложения try: main() except Exception as err: print(err)
Осталось только закомитить изменения в git
gii_plot_creator> git commit -m "application created"
Комментарии