#python #pandas #dataframe #matplotlib #plot
#python #панды #фрейм данных #matplotlib #построение
Вопрос:
Я хочу создать «ractangle-plot» с помощью matplotlib на python. К сожалению, у меня нет образца изображения, поэтому я должен описать свою проблему наилучшим образом.
Входные данные представляют собой df с разными временными интервалами и числами:
df = pd.DataFrame({'timedelta':[75,80,55,20,45],
'numb1':[8,25,11,14,8],
'timedelta1': [55,60,45,15,30],
'numb2':[10,30,15,18,10],
'timedelta2': [45,50,35,10,20]})
df['timedelta'] = pd.to_timedelta(df['timedelta'], unit='T')
df['timedelta1'] = pd.to_timedelta(df['timedelta1'], unit='T')
df['timedelta2'] = pd.to_timedelta(df['timedelta2'], unit='T')
Вывод:
timedelta numb1 timedelta1 numb2 timedelta2
0 01:15:00 8 00:55:00 10 00:45:00
1 01:20:00 25 01:00:00 30 00:50:00
2 00:55:00 11 00:45:00 15 00:35:00
3 00:20:00 14 00:15:00 18 00:10:00
4 00:45:00 8 00:30:00 10 00:20:00
Теперь я хочу визуализировать этот df. я хотел бы иметь прямоугольник с numb вверх и timedelta вправо — для каждой строки новый график.
Например, строка = 0:
rectangle (overall):
width = timedelta (=01:15:00)
hight = numb2 (=10)
внутри этого прямоугольника — два меньших прямоугольника разных цветов (все прямоугольники должны начинаться с одной и той же точки в нижнем левом углу)
width1 = timedelta1
hight1 = numb1
width2 = timedelta2
hight2 = numb2
Мне также понадобится метка для осей (высота и ширина) и прямоугольников.
Редактировать
с помощью следующего кода мне удалось создать нужные прямоугольники.
Как я могу пометить свои оси в соответствии с отдельными прямоугольниками (желательно с фигурными скобками). Мне здесь нужны не значения, а имена используемых столбцов.
for row in df.index:
fig = plt.figure()
ax = fig.add_axes([0,0,1,1])
p = patches.Rectangle((0,0), 1, 1, color= 'grey', alpha=0.2)
x1 = df['timedelta1'][row]/df['timedelta'][row]
y1 = df['numb1'][row]/df['numb2'][row]
p1 = patches.Rectangle((0,0), x1, y1, color= 'grey', alpha=0.8)
x2 = df['timedelta2'][row]/df['timedelta'][row]
y2 = df['numb2'][row]/df['numb2'][row]
p2 = patches.Rectangle((0,0), x2, y2, color= 'grey', alpha=0.5)
ax.add_patch(p)
ax.add_patch(p1)
ax.add_patch(p2)
ax.set_axis_off()
plt.show()
вот мой предыдущий график, теперь я хотел бы обозначить ширину и высоту каждого прямоугольника соответствующими именами столбцов (= timedelta/1/2
amp; numb1/2
) (желательно с фигурной скобкой)
Комментарии:
1. Спасибо за ссылку. Я нашел это для создания фигурной скобки: ссылка , но в моем случае я не знаю, что установить для осей — с помощью
ax[0]
I got an ‘Axes’ object is not subscriptable Ошибка.2. Это сработало бы, но я бы предпочел вместо меток осей фигурные скобки (как в CurlyBrace ), которые отмечают высоту и ширину каждого прямоугольника. И, используя пример кода ссылки, я получил ошибку
3. теперь я попытался следовать вашим инструкциям, но с подзаголовками мне не удается создавать свои графики. Я еще не удосужился вставить фигурные скобки….
4. Извините, но мне не удается настроить цикл for в соответствии с вашими советами. Лучший результат, которого я достиг до сих пор, — это мой цикл, который я прикрепил в качестве РЕДАКТИРОВАНИЯ к своему вопросу.
Ответ №1:
Вот мой ответ:
Единственное, что я еще не сделал, это расположить подзаголовки в табличном виде — like fig, axes = plt.subplots(x,y)
…
df = pd.DataFrame({'timedelta':[75,80,55,20,45],
'numb1':[8,25,11,14,8],
'timedelta1': [55,60,45,15,30],
'numb2':[10,30,15,18,10],
'timedelta2': [45,50,35,10,20]})
n = len(df.index)
fig, axes = plt.subplots(n , figsize=(5,20))
for i, ax in enumerate(axes):
p = patches.Rectangle((0,0), 1, 1, color= 'grey', alpha=0.2)
x1 = df['timedelta1'][i]/df['timedelta'][i]
y1 = df['numb1'][i]/df['numb2'][i]
p1 = patches.Rectangle((0,0), x1, y1, color= 'grey', alpha=0.8)
x2 = df['timedelta2'][i]/df['timedelta'][i]
y2 = df['numb2'][i]/df['numb2'][i]
p2 = patches.Rectangle((0,0), x2, y2, color= 'grey', alpha=0.5)
ax.add_patch(p)
ax.add_patch(p1)
ax.add_patch(p2)
#td
pe_a = [0.0, -0.2]
pe_b = [1, -0.2]
#td1
pe_1a = [0.0, -0.1]
pe_1b = [x1, -0.1]
#td2
pe_2a = [0.0, 0.0]
pe_2b = [x2, 0.0]
# fontdict for curly bracket 1 text
font = {'family': 'serif',
'color': 'k',
'weight': 'bold',
'style': 'italic',
'size': 10,
}
# coefficient for curly
k_r1 = 0.02
# td - Brace
curlyBrace.curlyBrace(fig, ax, pe_b, pe_a, k_r1, bool_auto=True, str_text='td', color='black', lw=1, int_line_num=1, fontdict=font)
curlyBrace.curlyBrace(fig, ax, pe_1b, pe_1a, k_r1, bool_auto=True, str_text='td1', color='black', lw=1, int_line_num=1, fontdict=font)
curlyBrace.curlyBrace(fig, ax, pe_2b, pe_2a, k_r1, bool_auto=True, str_text='td2', color='black', lw=1, int_line_num=1, fontdict=font)
# numb1
h_1a = [0.0, y1]
h_1b = [0.0, 0,0]
# numb2
h_2a = [-0.1, y2]
h_2b = [-0.1, 0,0]
# numb - Brace
curlyBrace.curlyBrace(fig, ax, h_1b, h_1a, k_r1, bool_auto=True, str_text='numb1', color='black', lw=1, int_line_num=1, fontdict=font)
curlyBrace.curlyBrace(fig, ax, h_2b, h_2a, k_r1, bool_auto=True, str_text='numb2', color='black', lw=1, int_line_num=1, fontdict=font)
ax.set_axis_off()
ax.legend(bbox_to_anchor=(1.04,1), loc="upper left")
plt.tight_layout()
plt.show()
Комментарии:
1. Неплохо. Что касается распределения ваших подзаголовков — эта
trim_axs(axs, N)
функция показывает вам , как удалить неиспользуемыеax
элементы из вашего рисунка после вычисления количества строк и столбцов, которые вам нужны.col = int(sqrt(len(df)/1.414))
дает приблизительный макет формата А4.2. Спасибо за вашу помощь @Mr. T — теперь у меня есть мои прямоугольники 🙂
Ответ №2:
Вот мое решение с использованием Matplotlib, Pandas и NumPy:
Сначала я загружаю данные, НО я сохраняю временные значения в секундах и сортирую от наименьшего к наибольшему:
Код:
df = pd.DataFrame({'timedelta':[75,80,55,20,45],
'numb1':[8,25,11,14,8],
'timedelta1': [55,60,45,15,30],
'numb2':[10,30,15,18,10],
'timedelta2': [45,50,35,10,20]})
df = df.sort_values(by='timedelta')
df
Вот df
после выполнения этого кода:
timedelta numb1 timedelta1 numb2 timedelta2
3 20 14 15 18 10
4 45 8 30 10 20
2 55 11 45 15 35
0 75 8 55 10 45
1 80 25 60 30 50
Затем я определяю вспомогательные функции для преобразования секунд в формат минут (позже используется в метках xtick)
def minSec(sec):
minutes = int(sec/60)
remSec = int(sec - 60*minutes)
if (remSec == 0):
remSec = '00'
else:
remSec = str(remSec)
return str(minutes) ':' remSec
def minSec_arr(sec_arr):
output = []
for i in range(sec_arr.shape[0]):
output.append(minSec(sec_arr[i]))
return output
Наконец, мы перебираем все строки df
и строим правильные прямоугольные графики с метками
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
import numpy as np
# Create MULTIPLE figures
for i in range(df.shape[0]):
# Get initial data
row = df.iloc[i,:]
height = max(row.numb1, row.numb2)
width = row.timedelta
# Create figure
fig, ax = plt.subplots()
currentAxis = plt.gca()
# Set basic layout
plt.xlim(0, max(df.timedelta))
plt.ylim(0, max(df.numb2))
plt.title('Row ' str(i))
plt.xlabel('Time [mm:ss]')
plt.ylabel('Number')
# Create proper x ticks
spaces = np.linspace(0, max(df.timedelta), 9)
vals = minSec_arr(spaces)
plt.xticks(spaces, vals)
# Create the main rectangle
currentAxis.add_patch(Rectangle((0, 0), width, height, fill=True, color='r', alpha=1, ec='k'))
axLabel = '(' str(minSec(width)) ',' str(height) ')'
plt.annotate(axLabel, (width,height))
# Create numb1 rectangle
width = row.timedelta1
height = row.numb1
currentAxis.add_patch(Rectangle((0, 0), width, height, fill=True, color='b', alpha=1, ec='k'))
axLabel = '(' str(minSec(width)) ',' str(height) ')'
plt.annotate(axLabel, (width,height))
# Create numb2 rectangle
width = row.timedelta2
height = row.numb2
currentAxis.add_patch(Rectangle((0, 0), width, height, fill=True, color='g', alpha=0.4, ec='k'))
axLabel = '(' str(minSec(width)) ',' str(height) ')'
plt.annotate(axLabel, (width,height))
plt.show()
Надеюсь, я полностью понял ваш вопрос
Комментарии:
1. Спасибо @Jacob за ваш ответ. Это хорошо, но не совсем то, что я хотел бы. Я добавил график, которого я достиг до сих пор, к своему вопросу выше, теперь я хотел бы обозначить стороны прямоугольника (ширина и высота) без добавления полной метки оси (как в вашем примере). Можете ли вы мне здесь помочь?