#python #matplotlib #plot #graph
#python #matplotlib #сюжет #График
Вопрос:
Я пытаюсь добиться этого идеального графического форматирования графика:
Я успешно сгенерировал график, также каким-то образом ( plt.pcolor
) перезаписал цвета графика по умолчанию (что, возможно, излишне, но я не могу найти другой вариант), но я борюсь с этим:
- Следующий цветовой слой имеет смещение к основной сетке
- Я также понятия не имею, как удалить все галочки (sticks) сверху, слева, вниз, но сохранить метки
- И я хотел бы сместить верхние метки еще ниже
- Я также хочу добавить 3 условия для цвета. Синий цвет также может быть постепенным синим в зависимости от первого цвета графика
Вот функциональный код для генерации графика (на среднем рисунке):
import numpy as np
import pandas as pd
from datetime import datetime as dt
from datetime import timedelta
import matplotlib
import matplotlib.pyplot as plt
from calendar import monthrange
import matplotlib.colors as mcolors
def create_graph(all_rows, task_names, farmers):
harvest = np.array(all_rows)
fig, ax = plt.subplots()
im = ax.imshow(harvest)
# We want to show all ticks...
ax.set_xticks(np.arange(len(farmers)))
ax.set_yticks(np.arange(len(task_names)))
# ... and label them with the respective list entries
ax.set_xticklabels(farmers, fontsize=4)
ax.set_yticklabels(task_names, fontsize=8)
# Rotate the tick labels and set their alignment.
plt.setp(ax.get_xticklabels(), rotation=0, ha="center", rotation_mode="anchor")
ax.tick_params(top=True, bottom=False, labeltop=True, labelbottom=False)
#Turn spines off and create white grid.
for edge, spine in ax.spines.items():
spine.set_visible(False)
cmap, norm = mcolors.from_levels_and_colors([0, 1, 4], ['grey', 'green'])
plt.pcolor(harvest, cmap=cmap, norm=norm, snap=True)
ax.set_xticks(np.arange(harvest.shape[1] 1)-.5, minor=True)
ax.set_yticks(np.arange(harvest.shape[0] 1)-.5, minor=True)
ax.grid(which="minor", color="w", linestyle='-', linewidth=1)
# ax.tick_params(which="minor", bottom=False, left=False)
# ax.set_title("Harvest of local farmers (in tons/year)")
fig.tight_layout()
plt.show()
plt.close()
vegetables = ["cucumber", "tomato", "lettuce", "asparagus",
"potato", "wheat", "barley"]
farmers = [1, 2, 3, 4, 5, 6, 7]
harvest = np.array([[0.8, 2.4, 2.5, 3.9, 0.0, 4.0, 0.0],
[2.4, 0.0, 4.0, 1.0, 2.7, 0.0, 0.0],
[1.1, 2.4, 0.8, 4.3, 1.9, 4.4, 0.0],
[0.6, 0.0, 0.3, 0.0, 3.1, 0.0, 0.0],
[0.7, 1.7, 0.6, 2.6, 2.2, 6.2, 0.0],
[1.3, 1.2, 0.0, 0.0, 0.0, 3.2, 5.1],
[0.1, 2.0, 0.0, 1.4, 0.0, 1.9, 6.3]])
create_graph(harvest, vegetables, farmers)
Я пытался посмотреть в matplotlib, но я не стал мудрее, чем раньше.
Комментарии:
1. Вы рисуете это не для большой матрицы, а только для иллюстраций?
Ответ №1:
Некоторые замечания:
- Чтобы убрать все галочки:
ax.tick_params(which='both', length=0)
. Обратите внимание, что вы можете вызыватьax.tick_params
много раз с разными параметрами. Метки тиков будут автоматически установлены ближе к графику. По умолчанию он работает только с основными тактами, поэтомуwhich='both'
он также работает с второстепенными тактами. imshow
имеет границы на половинках, которые красиво размещают целочисленные позиции в центре каждой ячейки.pcolor
(иpcolormesh
) предположим, что сетка позиций (по умолчанию для целых чисел), поэтому она плохо согласуется сimshow
.- Когда второстепенные тики перекрываются с основными, они подавляются. Итак, в этом случае работает простая установка их на значения, кратные
0.5
. vmin
иvmax
укажите, какое число соответствует самому низкому, а какое — самому высокому цвету цветовой карты.over color
может быть установлен для всех значений, превышающихvmax
. Это значение может быть отображено на цветовой панели черезextend='max'
. (По умолчанию для всех значений, превышающихvmax
, используется только самый высокий цвет, и аналогично самый низкий цвет для значений нижеvmin
). Обратите внимание, что если вы используете стандартную цветовую карту и хотите использоватьset_over
, вам нужно сделать копию цветовой карты (потому чтоset_over
изменяется цветовая карта на месте, и вы бы хотели, чтобы другие графики не были затронуты).- Цветовая карта по умолчанию называется ‘viridis’. Возможны многие другие.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
from matplotlib.ticker import MultipleLocator
def create_graph(all_rows, task_names, farmers):
harvest = np.array(all_rows)
fig, ax = plt.subplots()
# We want to show all ticks...
ax.set_xticks(np.arange(len(farmers)))
ax.set_yticks(np.arange(len(task_names)))
# ... and label them with the respective list entries
ax.set_xticklabels(farmers, fontsize=4)
ax.set_yticklabels(task_names, fontsize=8)
# Rotate the tick labels and set their alignment.
plt.setp(ax.get_xticklabels(), rotation=0, ha="center", rotation_mode="anchor")
ax.tick_params(top=True, bottom=False, labeltop=True, labelbottom=False)
ax.tick_params(which='both', length=0) # hide tick marks
ax.xaxis.set_minor_locator(MultipleLocator(0.5)) # minor ticks at halves
ax.yaxis.set_minor_locator(MultipleLocator(0.5)) # minor ticks at halves
# Turn spines off and create white grid.
for edge, spine in ax.spines.items():
spine.set_visible(False)
cmap = mcolors.LinearSegmentedColormap.from_list("grey-blue", ['grey', 'steelblue'])
cmap.set_over('green')
im = ax.imshow(harvest, cmap=cmap, vmin=0, vmax=1)
ax.grid(which="minor", color="w", linestyle='-', linewidth=1)
# ax.tick_params(which="minor", bottom=False, left=False)
# ax.set_title("Harvest of local farmers (in tons/year)")
plt.colorbar(im, extend='max' , ax=ax)
fig.tight_layout()
plt.show()
#plt.close()
vegetables = ["cucumber", "tomato", "lettuce", "asparagus",
"potato", "wheat", "barley"]
farmers = [1, 2, 3, 4, 5, 6, 7]
harvest = np.array([[0.8, 2.4, 2.5, 3.9, 0.0, 4.0, 0.0],
[2.4, 0.0, 4.0, 1.0, 2.7, 0.0, 0.0],
[1.1, 2.4, 0.8, 4.3, 1.9, 4.4, 0.0],
[0.6, 0.0, 0.3, 0.0, 3.1, 0.0, 0.0],
[0.7, 1.7, 0.6, 2.6, 2.2, 6.2, 0.0],
[1.3, 1.2, 0.0, 0.0, 0.0, 3.2, 5.1],
[0.1, 2.0, 0.0, 1.4, 0.0, 1.9, 6.3]])
create_graph(harvest, vegetables, farmers)
Приведенный выше код создает цветовую карту, которая плавно переходит от серого при 0 к синему при 1. Если вам нужны специальные цвета для значений 0 и 1 и более стандартная цветовая карта для промежуточных значений, вы могли бы работать с ‘over’, ‘under’ и ‘bad’ цветом. (‘bad’ предназначен для значений, которые бесконечны или не являются числом.) Вы могли бы сделать копию матрицы ‘harvest’ и изменить высокие значения на ‘np.nan’, чтобы придать им особый цвет. К сожалению, нет простого способа показать плохой цвет на цветовой панели, но цвет «под» и «над» можно отобразить с помощью extend='both'
и сделать прямоугольным (вместо треугольного) с помощью extendrect=True
.
from copy import copy
cmap = copy(plt.get_cmap('hot'))
cmap.set_under('grey')
cmap.set_over('steelblue')
cmap.set_bad('green')
im = ax.imshow(np.where(harvest <= 1, harvest, np.nan), cmap=cmap, vmin=0.000001, vmax=0.999999)
plt.colorbar(im, extend='both', extendrect=True, ticks=np.arange(0, 1.01, .1), ax=ax)
Комментарии:
1. Большое вам спасибо за отличный ответ с множеством вариантов на выбор! Я особенно благодарен за ваше объяснение функций и за ссылку на colormaps. Это действительно помогает мне понять то, что не было понято раньше.
Ответ №2:
Я думаю, plt.pcolor
это перебор. Все, что вам нужно сделать, это настроить цветовую карту и применить к imshow
. Вот мое решение, основанное на вашем коде.
import numpy as np
from datetime import datetime as dt
from datetime import timedelta
import matplotlib
import matplotlib.pyplot as plt
from calendar import monthrange
from matplotlib.colors import ListedColormap, to_rgba
def create_graph(all_rows, task_names, farmers):
harvest = np.array(all_rows)
# you have to customize the colormap
rgba = plt.cm.viridis(range(100))
ind = int(1/harvest.max()*100)
rgba[0] = to_rgba('gray')
rgba[ind 1:] = to_rgba('green')
cmap1 = ListedColormap(rgba)
fig, ax = plt.subplots()
ax.imshow(harvest, cmap=cmap1)
# We want to show all ticks...
ax.set_xticks(np.arange(len(farmers)))
ax.set_yticks(np.arange(len(task_names)))
# ... and label them with the respective list entries
ax.set_xticklabels(farmers, fontsize=4)
ax.set_yticklabels(task_names, fontsize=8)
# Rotate the tick labels and set their alignment.
plt.setp(ax.get_xticklabels(), rotation=0, ha="center", rotation_mode="anchor")
ax.tick_params(top=True, bottom=False, labeltop=True, labelbottom=False)
#Turn spines off and create white grid.
for edge, spine in ax.spines.items():
spine.set_visible(False)
ax.set_xticks(np.arange(harvest.shape[1] 1)-0.5, minor=True)
ax.set_yticks(np.arange(harvest.shape[0] 1)-0.5, minor=True)
ax.grid(which="minor", color="w", linestyle='-', linewidth=1)
# ax.tick_params(which="minor", bottom=False, left=False)
# ax.set_title("Harvest of local farmers (in tons/year)")
fig.tight_layout()
vegetables = ["cucumber", "tomato", "lettuce", "asparagus",
"potato", "wheat", "barley"]
farmers = [1, 2, 3, 4, 5, 6, 7]
harvest = np.array([[0.8, 2.4, 2.5, 3.9, 0.0, 4.0, 0.0],
[2.4, 0.0, 4.0, 1.0, 2.7, 0.0, 0.0],
[1.1, 2.4, 0.8, 4.3, 1.9, 4.4, 0.0],
[0.6, 0.0, 0.3, 0.0, 3.1, 0.0, 0.0],
[0.7, 1.7, 0.6, 2.6, 2.2, 6.2, 0.0],
[1.3, 1.2, 0.0, 0.0, 0.0, 3.2, 5.1],
[0.1, 2.0, 0.0, 1.4, 0.0, 1.9, 6.3]])
create_graph(harvest, vegetables, farmers)
Комментарии:
1. Большое спасибо за ваше решение, это еще более минималистичный подход, чем предыдущий метод!
Ответ №3:
Это результирующий график, составленный по обоим вашим предложениям!
С чем я борюсь, так это с точной настройкой цветовой карты. Из всех цветовых карт я выбрал эту, но более низкие значения слишком яркие. Есть ли какой-либо способ обрезать цветовую карту, чтобы она не начиналась с этого желтого цвета, но начиналась в диапазоне, который я рисую (красный квадрат)?
Комментарии:
1. Это строка, решающая последнюю задачу!
cmap = matplotlib.colors.LinearSegmentedColormap.from_list("", ["#6ECC84","#00492B"])
Еще раз спасибо за вашу огромную помощь!