matplotlib преобразует df в прямоугольник

#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()
  

Вот пример выходного рисунка
Row1_Rect_Plot

Надеюсь, я полностью понял ваш вопрос

Комментарии:

1. Спасибо @Jacob за ваш ответ. Это хорошо, но не совсем то, что я хотел бы. Я добавил график, которого я достиг до сих пор, к своему вопросу выше, теперь я хотел бы обозначить стороны прямоугольника (ширина и высота) без добавления полной метки оси (как в вашем примере). Можете ли вы мне здесь помочь?