Можно ли динамически добавлять оси в макет graph_objects в plotly?

#python #pandas #plotly #plotly.graph-objects

Вопрос:

В моем практическом обучении, которое я сейчас прохожу, есть скрипт python, который считывает файл CSV и отображает графики по выбранным столбцам этого файла. Однако выбор заголовков жестко запрограммирован, поэтому, если кто-то хочет использовать скрипт, он должен манипулировать кодом. Моя задача — сделать все это динамическим, например, пользователь скрипта может выбрать любое количество столбцов с помощью console ( argparse ), и скрипт автоматически создает трассировки, создает макет, добавляет оба к рисунку и экспортирует его в HTML-файл.

Мне удалось выполнить все это, за исключением части макета. В текущем (жестко закодированном) состоянии скрипта эти аргументы передаются graph_objects.Layout функции:

  layout = go.Layout(title=inFile,
                       plot_bgcolor='rgb(230, 230,230)', showlegend=True,
                       yaxis=dict(
                           title=df.columns[y1graph] # Note: 'ygraph' contains the index of the column
                       ),
                       yaxis2=dict(
                            title=df.columns[y2graph],
                            side='right',
                            overlaying='y'
                       ),
                       yaxis3=dict(
                            title=df.columns[y3graph],
                            side='right',
                            overlaying='y'
                       )
                    )
 

К сожалению, я не нашел способа сделать все это динамичным, чтобы аргументы «yaxis» добавлялись в соответствии с количеством выбранных столбцов. Я также не нашел способа добавлять заголовки к графикам, заставлять их накладываться друг на друга и помещать их в правую сторону так же, как go.Layout это делается. Конечно, есть способ добавлять заголовки с помощью plotly express, но он как бы не делает то же самое для меня в отношении аргументов overlaying and side .

Есть идеи?

Пожалуйста, обратите внимание: это мой самый первый вопрос здесь, в stackoverflow, поэтому, если я сделал что-то не так, пожалуйста, посоветуйте! Кроме того, если я пропустил важную информацию, пожалуйста, дайте мне знать.

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

1. Ваш вопрос может быть закрыт по нескольким причинам. Вам не хватает полного фрагмента и данных для воссоздания вашей проблемы, и вы задаете несколько вопросов в одном сообщении. Итак, несколько разъяснений будут в порядке. Во-первых, это график, который вы построили make_subplots ?

2. ХОРОШО, я сделаю все возможное, чтобы все прояснить. Спасибо за ваш совет! Исходный скрипт создается с использованием plotly.graph_objects. К сожалению, я не могу поделиться CSV из-за соображений защиты данных, и я спрошу официальных лиц, разрешено ли мне делиться всем сценарием. Однако способ его работы очень прост: сначала создаются, например, три переменные трассировки, которые назначаются вызову go.Scatter. Затем макет создается так, как я показал в своем исходном сообщении. Затем фигура создается с помощью go. Рисунок, со всеми трассировками, переданными как данные вместе с макетом.

3. Я опубликую весь код как можно скорее.

4. Разве Роб Рэймонд не нашел решение вашего вопроса?

Ответ №1:

  • имитировать CSV, фрейм данных с 20 столбцами
  • имитировать выбор пользователем столбцов для построения графика (произвольная выборка 4 столбцов из 20)
  • постройте фигуру, назначив разные оси yaxis для каждой трассировки
  • наконец, согласно вопросу, динамически настраивайте оси y
 import numpy as np
import plotly.express as px
import pandas as pd

# simulate a CSV, 20 columns...
df = pd.DataFrame(
    {
        chr(ord("A")   i): np.random.uniform(miny, miny   200, 30)
        for i, miny in zip(range(20), np.random.randint(30, 3000, 20))
    }
)

# simulate user passing columns to plot...
cols = pd.Series(df.columns).sample(4).tolist()

# build figure with each trace using it's own yaxis
fig = px.line(df, y=cols).for_each_trace(
    lambda t: t.update(yaxis=f"y{cols.index(t.name) 1 if cols.index(t.name)>0 else ''}")
).update_layout(yaxis={"title":cols[0]})

# dynamically update yaxes...
fig.update_layout(
    {
        t.yaxis.replace("y", "yaxis"): {
            "title": t.name,
            "overlaying": "y",
            "side": "right",
            "position":1-(i/15)
        }
        for i,t in enumerate(fig.data)
        if t.yaxis != "y"
    }
).update_layout(xaxis={"domain":[0,1-(len(cols)*.05)]}) # give some space for additional yaxes
 

введите описание изображения здесь