Matplotlib chart - создание горизонтальной гистограммы
Я наткнулся на следующий фрагмент, создав горизонтальную гистограмму с использованием matplotlib:
import matplotlib
from pylab import *
val = 3+10*rand(5) # the bar lengths
pos = arange(5)+.5 # the bar centers on the y axis
print pos
figure(1)
barh(pos,val, align='center')
yticks(pos, ('Tom', 'Dick', 'Harry', 'Slim', 'Jim'))
xlabel('Performance')
title('horizontal bar chart using matplotlib')
grid(True)
show()
Я хочу изменить приведенный выше script следующим образом:
- Сделать стробированные полосы "менее короткими" (т.е. уменьшить высоту построенных горизонтальных баров)
- Поместите как отрицательные, так и положительные числа в виде горизонтальных полос на одном и том же участке
любая помощь (фрагмент кода или ссылки), чтобы помочь мне сделать вышеуказанные изменения очень полезными.
как в сторону, если бы я хотел сделать штабелированные горизонтальные бары (скажем, каждая метка имела 3 уложенных горизонтальных полос), как бы я мог изменить вышеприведенный код, чтобы построить график из трех штабелированных горизонтальных полос?
[[Edit]]
Может ли кто-нибудь опубликовать два коротких фрагмента кода, которые показывают, как:
-
Печать меток на противоположной стороне горизонтальных полос (так, например, метка для "отрицательных" баров появляется в первом квартете, а метки для "положительных" баров отображаются во втором квадранте
-
График (например, 2 или 3) горизонтальных бара (вместо одного). Хорошими примерами являются первые два изображения, показанные здесь
Ответы
Ответ 1
import matplotlib
from pylab import *
val = 3-6*rand(5) # the bar lengths # changed your data slightly
pos = arange(5)+.5 # the bar centers on the y axis
print pos
figure(1)
barh(pos,val, align='center',height=0.1) # notice the 'height' argument
yticks(pos, ('Tom', 'Dick', 'Harry', 'Slim', 'Jim'))
gca().axvline(0,color='k',lw=3) # poor man zero level
xlabel('Performance')
title('horizontal bar chart using matplotlib')
grid(True)
show()
В общем, я бы предложил не использовать from pyplot import *
. Если вы не находитесь в интерактивном режиме, используйте объектно-ориентированный подход:
import matplotlib.pyplot as plt
from numpy.random import rand
from numpy import arange
val = 3-6*rand(5) # the bar lengths
pos = arange(5)+.5 # the bar centers on the y axis
print pos
fig = plt.figure()
ax = fig.add_subplot(111)
ax.barh(pos,val, align='center',height=0.1)
ax.set_yticks(pos, ('Tom', 'Dick', 'Harry', 'Slim', 'Jim'))
ax.axvline(0,color='k',lw=3) # poor man zero level
ax.set_xlabel('Performance')
ax.set_title('horizontal bar chart using matplotlib')
ax.grid(True)
plt.show()
Хорошей отправной точкой для различных видов графиков является matplotlib
галерея
Ответ 2
Как сказал Женя, вам придется поддразнивать свой сюжет.
В качестве примера ниже приведена функция, которая создает настраиваемый график горизонтальной полосы:
- ввод - это данные, заключенные в словаре
- он затем вычисляет положение Y тиков в соответствии с количеством мер (бар), которое у вас есть в каждой категории (люди), и местом, которое вы хотите разместить между каждой категорией.
- наконец, он отображает каждую из мер данных (с другим цветом, если вы указали это)
По умолчанию будет отображаться имя категорий (людей) справа, но вы можете, конечно, изменить это.
import numpy as np
import matplotlib.pyplot as plt
# creation of the data
name_list = ['day1', 'day2', 'day3', 'day4']
data = {name: 3+10*np.random.rand(5) for name in name_list}
colors_list = ['0.5', 'r', 'b', 'g'] #optional
def customize_barh(data, width_bar=1, width_space=0.5, colors=None):
n_measure = len(data) #number of measure per people
n_people = data[data.keys()[0]].size # number of people
#some calculation to determine the position of Y ticks labels
total_space = n_people*(n_measure*width_bar)+(n_people-1)*width_space
ind_space = n_measure*width_bar
step = ind_space/2.
pos = np.arange(step, total_space+width_space, ind_space+width_space)
# create the figure and the axes to plot the data
fig = plt.figure(figsize=(8,6))
ax = fig.add_axes([0.15, 0.15, 0.65, 0.7])
# remove top and right spines and turn ticks off if no spine
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('right') # ticks position on the right
# postition of tick out
ax.tick_params(axis='both', direction='out', width=3, length=6,
labelsize=24, pad=8)
ax.spines['left'].set_linewidth(3)
ax.spines['bottom'].set_linewidth(3)
# plot the data
for i,day in enumerate(data.keys()):
if colors == None:
ax.barh(pos-step+i*width_bar, data[day], width_bar, #facecolor='0.4',
edgecolor='k', linewidth=3)
else:
ax.barh(pos-step+i*width_bar, data[day], width_bar, facecolor=colors[i],
edgecolor='k', linewidth=3)
ax.set_yticks(pos)
# you may want to use the list of name as argument of the function to be more
# flexible (if you have to add a people)
ax.set_yticklabels(('Tom', 'Dick', 'Harry', 'Slim', 'Jim'))
ax.set_ylim((-width_space, total_space+width_space))
ax.set_xlabel('Performance', size=26, labelpad=10)
customize_barh(data, colors=colors_list)
plt.savefig('perf.png')
plt.show()
который производит:
![this]()
Ответ 3
Следующий фрагмент кода - это пример использования текстовой функции для комментирования текстовой метки в левой части для отрицательных значений, а справа - для положительных значений, как указано как gcalmettes, так и Zhenya.
from pylab import setp
import numpy as np
import matplotlib.pyplot as plt
import math
# creation of the data
name_list = ['day1', 'day2', 'day3', 'day4']
data = {name: 3+10*np.random.rand(5) for name in name_list}
for name in name_list:
data[name][0] = data[name][0]*-1
data[name][2] = data[name][2]*-1
colors_list = ['0.5', 'r', 'b', 'g'] #optional
def customize_barh(data, width_bar=1, width_space=0.5, colors=None):
n_measure = len(data) #number of measure per people
n_people = data[data.keys()[0]].size # number of people
#some calculation to determine the position of Y ticks labels
total_space = n_people*(n_measure*width_bar)+(n_people-1)*width_space
ind_space = n_measure*width_bar
step = ind_space/2.
pos = np.arange(step, total_space+width_space, ind_space+width_space)
# create the figure and the axes to plot the data
fig = plt.figure(figsize=(8,6))
ax = fig.add_axes([0.15, 0.15, 0.65, 0.7])
# remove top and right spines and turn ticks off if no spine
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('default') # ticks position on the right
# postition of tick out
ax.tick_params(axis='both', direction='out', width=3, length=6,
labelsize=24, pad=8)
ax.spines['left'].set_linewidth(3)
ax.spines['bottom'].set_linewidth(3)
# plot the data
for i,day in enumerate(data.keys()):
if colors == None:
ax.barh(pos-step+i*width_bar, data[day], width_bar, #facecolor='0.4',
edgecolor='k', linewidth=3)
else:
ax.barh(pos-step+i*width_bar, data[day], width_bar, facecolor=colors[i],
edgecolor='k', linewidth=3)
ax.set_yticks(pos)
# you may want to use the list of name as argument of the function to be more
# flexible (if you have to add a people)
setp(ax.get_yticklabels(), visible=False)
ax.set_ylim((-width_space, total_space+width_space))
ax.set_xlabel('Performance', size=26, labelpad=10)
labels_list = ['Tom', 'Dick', 'Harry', 'Slim','Jim']
# creation of an array of positive/negative values (based on the values
# of the data) that will be used as x values for adding text as side labels
side_list = []
for index in range(len(labels_list)):
sum = 0
for name in name_list:
sum+= data[name][index]
if math.copysign(1,sum) > 0:
side_list.append(16)
else:
side_list.append(-21)
for label in labels_list:
plt.text(side_list[labels_list.index(label)], pos[labels_list.index(label)]-0.5, label,fontsize=26)
customize_barh(data, colors=colors_list)
plt.savefig('perf.png')
plt.show()
Это работает на том основании, что все бары для данного человека должны быть отрицательными или положительными для текста, который будет аннотирован на правильной стороне. Чтобы изменить это поведение, просто измените генерацию side_list.
Например, если вы хотите, чтобы определенный барный порог определял положение метки, затем подсчитывайте значения данных над этим порогом вместо суммирования значений для данного имени.
Например, для порогового значения 3 бара из сколько угодно, цикл for становится
for index in range(len(labels_list)):
count = 0
for name in name_list:
if data[name][index] > 0:
count+= 1
if count > 3:
side_list.append(16)
else:
side_list.append(-21)
Генерация side_list также потребуется изменить, чтобы настроить диапазон данных, поскольку приведенные примеры используют случайные данные в указанном диапазоне.
Например, вам нужно будет отрегулировать смещения метки side_list.append(16)
и side_list.append(-21)
в соответствии с вашими данными.