Графически: создание разброса с категориальным джиттером по оси x и многоуровневой осью

Я хотел бы построить график с многоуровневой осью x, как на следующем рисунке: Многоуровневый разброс

import plotly.graph_objects as go
fig = go.Figure()
fig.add_trace(
  go.Scatter(
    x = [df['x'], df['x1']],
    y = df['y'],
    mode='markers'
  )
)

Но также я хотел бы поместить джиттер на ось x, как на следующем рисунке: введите описание изображения здесь

Пока я могу сделать каждый график независимо, используя следующий код:

import plotly.express as px
fig = px.strip(df,
               x=[df["x"], df['x1']], 
               y="y",
               stripmode='overlay') 

Можно ли совместить джиттер и многоуровневую ось на одном графике?

Вот код для воспроизведения набора данных:

import numpy as np
import pandas as pd
import random

'''Create DataFrame'''
price = np.append(
  np.random.normal(20, 5, size=(1, 50)), np.random.normal(40, 2, size=(1, 10))
)
quantity = np.append(
  np.random.randint(1, 5, size=(50)), np.random.randint(8, 12, size=(10))
)

firstLayerList = ['15 in', '16 in']
secondLayerList = ['1/2', '3/8']
vendorList = ['Vendor1','Vendor2','Vendor3']

data = {
  'Width':  [random.choice(firstLayerList) for i in range(len(price))],
  'Length': [random.choice(secondLayerList) for i in range(len(price))],
  'Vendor': [random.choice(vendorList) for i in range(len(price))],
  'Quantity': quantity,
  'Price':  price
}
df = pd.DataFrame.from_dict(data)

person Daniel Zapata    schedule 27.11.2020    source источник
comment
Предоставьте образец данных для воспроизведения ваших цифр.   -  person vestland    schedule 28.11.2020
comment
И есть @vestland - всегда на шаг впереди в вопросах Plotly. На самом деле собирался отметить вас на этом ... но я вижу, в этом нет необходимости. :-)   -  person S3DEV    schedule 28.11.2020
comment
@ S3DEV Ха-ха =) Это оказалось слишком сложно для субботнего вечера. Придется отложить это на некоторое время. Если, конечно, ОП не предоставляет образец данных. Потому что в том-то и дело ... Я всегда трачу больше времени на воссоздание проблемы, чем на ее решение, поэтому от хороших вопросов с правильными выборками данных всегда сложнее отказаться. Кстати, мне очень понравилась ваша недавняя работа над осями y < / а>.   -  person vestland    schedule 28.11.2020
comment
@vestland - Согласен! Иногда с этими синтетическими наборами данных бывает сложно. Очень признателен, очень любезен с вашей стороны. График в итоге оказался беспорядочным, но вопрос решил. Позже товарищ.   -  person S3DEV    schedule 28.11.2020
comment
Я добавил код для создания образца данных. Спасибо!   -  person Daniel Zapata    schedule 29.11.2020
comment
Спасибо, Даниэль. Можно ли было бы комбинировать метки xaxis, чтобы облегчить себе жизнь? Например, «15 3/8», «15 1/2», «16», ...?   -  person S3DEV    schedule 29.11.2020
comment
@ S3DEV Я бы предпочел не делать этого, но не возражаю, если это окажется единственным вариантом.   -  person Daniel Zapata    schedule 29.11.2020
comment
Даниэль - Большое спасибо, так за ваше терпение в этом вопросе. Я дал вам ответ; надеюсь, это поможет вам.   -  person S3DEV    schedule 30.11.2020


Ответы (2)


Во-первых - спасибо за вызов! В наши дни не так много сложных вопросов о Plotly.

Ключевые элементы для создания диаграммы разброса с джиттером:

  • Использование mode: 'box' - для создания прямоугольной диаграммы, не диаграммы рассеяния.
  • Установка 'boxpoints': 'all' - так все точки нанесены.
  • Использование 'pointpos': 0 - для центрирования точек по оси x.
  • Removing (hiding!) the whisker boxes using:
    • 'fillcolor': 'rgba(255,255,255,0)'
    • 'line': {'color': 'rgba(255,255,255,0)'}

Подготовка DataFrame:

Этот код просто разбивает основной DataFrame на фрейм для каждого поставщика, таким образом позволяя создать трассировку для каждого со своим собственным цветом.

df1 = df[df['Vendor'] == 'Vendor1']
df2 = df[df['Vendor'] == 'Vendor2']
df3 = df[df['Vendor'] == 'Vendor3']

Код участка:

В коде построения можно использовать for-цикл, если хотите. Однако я намеренно сделал его более подробным, чтобы повысить ясность.

import plotly.io as pio

layout = {'title': 'Categorical X-Axis, with Jitter'}
traces = []

traces.append({'x': [df1['Width'], df1['Length']], 'y': df1['Price'], 'name': 'Vendor1', 'marker': {'color': 'green'}})
traces.append({'x': [df2['Width'], df2['Length']], 'y': df2['Price'], 'name': 'Vendor2', 'marker': {'color': 'blue'}})
traces.append({'x': [df3['Width'], df3['Length']], 'y': df3['Price'], 'name': 'Vendor3', 'marker': {'color': 'orange'}})

# Update (add) trace elements common to all traces.
for t in traces:
    t.update({'type': 'box',
              'boxpoints': 'all',
              'fillcolor': 'rgba(255,255,255,0)',
              'hoveron': 'points',
              'hovertemplate': 'value=%{x}<br>Price=%{y}<extra></extra>',
              'line': {'color': 'rgba(255,255,255,0)'},
              'pointpos': 0,
              'showlegend': True})

pio.show({'data': traces, 'layout': layout})

График:

Данные за этим графиком были сгенерированы с использованием np.random.seed(73) в соответствии с кодом создания набора данных, опубликованным в вопросе.

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

Комментарии (TL; DR):

В приведенном здесь примере кода используется API-интерфейс нижнего уровня Plotly, а не удобная оболочка, такая как graph_objects или express. Причина в том, что я (лично) считаю полезным для пользователей показать, что происходит «под капотом», а не маскировать базовую логику кода с помощью удобной оболочки.

Таким образом, когда пользователю нужно изменить более мелкие детали графика, он будет лучше понимать lists и dicts, которые Plotly создает для базового графического движка (orca).

И этот вариант использования является ярким примером этого рассуждения, поскольку он уводит Plotly за пределы своей (текущей) проектной точки.

person S3DEV    schedule 29.11.2020
comment
Это именно то, что мне было нужно! Большое спасибо @ S3DEV за вашу помощь! - person Daniel Zapata; 30.11.2020
comment
@DanielZapata - С удовольствием. Рада, что это работает для вас. - person S3DEV; 30.11.2020

В качестве альтернативного простого решения можно использовать: plotly.express.strip с stripmode="overlay" (подробнее о параметрах)

Здесь я покажу вам пример с данными Iris. Сюжетная версия 4.4.1.

import plotly.express as px
df = px.data.iris()
fig = px.strip(df, 
               x="species", y="sepal_width", color="species", 
               title="This is a stripplot!", 
               stripmode = "overlay"   # Select between "group" or "overlay" mode
)
fig.show()

Это результат (запустите фрагмент кода)

<div>
        
                <script type="text/javascript">window.PlotlyConfig = {MathJaxConfig: 'local'};</script>
        <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>    
            <div id="70e0d94a-4a4c-40fc-af77-95274959151b" class="plotly-graph-div" style="height:100%; width:100%;"></div>
            <script type="text/javascript">
                
                    window.PLOTLYENV=window.PLOTLYENV || {};
                    
                if (document.getElementById("70e0d94a-4a4c-40fc-af77-95274959151b")) {
                    Plotly.newPlot(
                        '70e0d94a-4a4c-40fc-af77-95274959151b',
                        [{"alignmentgroup": "True", "boxpoints": "all", "fillcolor": "rgba(255,255,255,0)", "hoverlabel": {"namelength": 0}, "hoveron": "points", "hovertemplate": "species=%{x}<br>sepal_width=%{y}", "legendgroup": "species=setosa", "line": {"color": "rgba(255,255,255,0)"}, "marker": {"color": "#636efa"}, "name": "species=setosa", "offsetgroup": "species=setosa", "orientation": "v", "pointpos": 0, "showlegend": true, "type": "box", "x": ["setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa", "setosa"], "x0": " ", "xaxis": "x", "y": [3.5, 3.0, 3.2, 3.1, 3.6, 3.9, 3.4, 3.4, 2.9, 3.1, 3.7, 3.4, 3.0, 3.0, 4.0, 4.4, 3.9, 3.5, 3.8, 3.8, 3.4, 3.7, 3.6, 3.3, 3.4, 3.0, 3.4, 3.5, 3.4, 3.2, 3.1, 3.4, 4.1, 4.2, 3.1, 3.2, 3.5, 3.1, 3.0, 3.4, 3.5, 2.3, 3.2, 3.5, 3.8, 3.0, 3.8, 3.2, 3.7, 3.3], "y0": " ", "yaxis": "y"}, {"alignmentgroup": "True", "boxpoints": "all", "fillcolor": "rgba(255,255,255,0)", "hoverlabel": {"namelength": 0}, "hoveron": "points", "hovertemplate": "species=%{x}<br>sepal_width=%{y}", "legendgroup": "species=versicolor", "line": {"color": "rgba(255,255,255,0)"}, "marker": {"color": "#EF553B"}, "name": "species=versicolor", "offsetgroup": "species=versicolor", "orientation": "v", "pointpos": 0, "showlegend": true, "type": "box", "x": ["versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor", "versicolor"], "x0": " ", "xaxis": "x", "y": [3.2, 3.2, 3.1, 2.3, 2.8, 2.8, 3.3, 2.4, 2.9, 2.7, 2.0, 3.0, 2.2, 2.9, 2.9, 3.1, 3.0, 2.7, 2.2, 2.5, 3.2, 2.8, 2.5, 2.8, 2.9, 3.0, 2.8, 3.0, 2.9, 2.6, 2.4, 2.4, 2.7, 2.7, 3.0, 3.4, 3.1, 2.3, 3.0, 2.5, 2.6, 3.0, 2.6, 2.3, 2.7, 3.0, 2.9, 2.9, 2.5, 2.8], "y0": " ", "yaxis": "y"}, {"alignmentgroup": "True", "boxpoints": "all", "fillcolor": "rgba(255,255,255,0)", "hoverlabel": {"namelength": 0}, "hoveron": "points", "hovertemplate": "species=%{x}<br>sepal_width=%{y}", "legendgroup": "species=virginica", "line": {"color": "rgba(255,255,255,0)"}, "marker": {"color": "#00cc96"}, "name": "species=virginica", "offsetgroup": "species=virginica", "orientation": "v", "pointpos": 0, "showlegend": true, "type": "box", "x": ["virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica", "virginica"], "x0": " ", "xaxis": "x", "y": [3.3, 2.7, 3.0, 2.9, 3.0, 3.0, 2.5, 2.9, 2.5, 3.6, 3.2, 2.7, 3.0, 2.5, 2.8, 3.2, 3.0, 3.8, 2.6, 2.2, 3.2, 2.8, 2.8, 2.7, 3.3, 3.2, 2.8, 3.0, 2.8, 3.0, 2.8, 3.8, 2.8, 2.8, 2.6, 3.0, 3.4, 3.1, 3.0, 3.1, 3.1, 3.1, 2.7, 3.2, 3.3, 3.0, 2.5, 3.0, 3.4, 3.0], "y0": " ", "yaxis": "y"}],
                        {"boxmode": "overlay", "legend": {"tracegroupgap": 0}, "template": {"data": {"bar": [{"error_x": {"color": "#2a3f5f"}, "error_y": {"color": "#2a3f5f"}, "marker": {"line": {"color": "#E5ECF6", "width": 0.5}}, "type": "bar"}], "barpolar": [{"marker": {"line": {"color": "#E5ECF6", "width": 0.5}}, "type": "barpolar"}], "carpet": [{"aaxis": {"endlinecolor": "#2a3f5f", "gridcolor": "white", "linecolor": "white", "minorgridcolor": "white", "startlinecolor": "#2a3f5f"}, "baxis": {"endlinecolor": "#2a3f5f", "gridcolor": "white", "linecolor": "white", "minorgridcolor": "white", "startlinecolor": "#2a3f5f"}, "type": "carpet"}], "choropleth": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "type": "choropleth"}], "contour": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "colorscale": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "type": "contour"}], "contourcarpet": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "type": "contourcarpet"}], "heatmap": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "colorscale": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "type": "heatmap"}], "heatmapgl": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "colorscale": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "type": "heatmapgl"}], "histogram": [{"marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "histogram"}], "histogram2d": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "colorscale": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "type": "histogram2d"}], "histogram2dcontour": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "colorscale": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "type": "histogram2dcontour"}], "mesh3d": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "type": "mesh3d"}], "parcoords": [{"line": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "parcoords"}], "pie": [{"automargin": true, "type": "pie"}], "scatter": [{"marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scatter"}], "scatter3d": [{"line": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scatter3d"}], "scattercarpet": [{"marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scattercarpet"}], "scattergeo": [{"marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scattergeo"}], "scattergl": [{"marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scattergl"}], "scattermapbox": [{"marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scattermapbox"}], "scatterpolar": [{"marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scatterpolar"}], "scatterpolargl": [{"marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scatterpolargl"}], "scatterternary": [{"marker": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "type": "scatterternary"}], "surface": [{"colorbar": {"outlinewidth": 0, "ticks": ""}, "colorscale": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "type": "surface"}], "table": [{"cells": {"fill": {"color": "#EBF0F8"}, "line": {"color": "white"}}, "header": {"fill": {"color": "#C8D4E3"}, "line": {"color": "white"}}, "type": "table"}]}, "layout": {"annotationdefaults": {"arrowcolor": "#2a3f5f", "arrowhead": 0, "arrowwidth": 1}, "coloraxis": {"colorbar": {"outlinewidth": 0, "ticks": ""}}, "colorscale": {"diverging": [[0, "#8e0152"], [0.1, "#c51b7d"], [0.2, "#de77ae"], [0.3, "#f1b6da"], [0.4, "#fde0ef"], [0.5, "#f7f7f7"], [0.6, "#e6f5d0"], [0.7, "#b8e186"], [0.8, "#7fbc41"], [0.9, "#4d9221"], [1, "#276419"]], "sequential": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "sequentialminus": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]]}, "colorway": ["#636efa", "#EF553B", "#00cc96", "#ab63fa", "#FFA15A", "#19d3f3", "#FF6692", "#B6E880", "#FF97FF", "#FECB52"], "font": {"color": "#2a3f5f"}, "geo": {"bgcolor": "white", "lakecolor": "white", "landcolor": "#E5ECF6", "showlakes": true, "showland": true, "subunitcolor": "white"}, "hoverlabel": {"align": "left"}, "hovermode": "closest", "mapbox": {"style": "light"}, "paper_bgcolor": "white", "plot_bgcolor": "#E5ECF6", "polar": {"angularaxis": {"gridcolor": "white", "linecolor": "white", "ticks": ""}, "bgcolor": "#E5ECF6", "radialaxis": {"gridcolor": "white", "linecolor": "white", "ticks": ""}}, "scene": {"xaxis": {"backgroundcolor": "#E5ECF6", "gridcolor": "white", "gridwidth": 2, "linecolor": "white", "showbackground": true, "ticks": "", "zerolinecolor": "white"}, "yaxis": {"backgroundcolor": "#E5ECF6", "gridcolor": "white", "gridwidth": 2, "linecolor": "white", "showbackground": true, "ticks": "", "zerolinecolor": "white"}, "zaxis": {"backgroundcolor": "#E5ECF6", "gridcolor": "white", "gridwidth": 2, "linecolor": "white", "showbackground": true, "ticks": "", "zerolinecolor": "white"}}, "shapedefaults": {"line": {"color": "#2a3f5f"}}, "ternary": {"aaxis": {"gridcolor": "white", "linecolor": "white", "ticks": ""}, "baxis": {"gridcolor": "white", "linecolor": "white", "ticks": ""}, "bgcolor": "#E5ECF6", "caxis": {"gridcolor": "white", "linecolor": "white", "ticks": ""}}, "title": {"x": 0.05}, "xaxis": {"automargin": true, "gridcolor": "white", "linecolor": "white", "ticks": "", "title": {"standoff": 15}, "zerolinecolor": "white", "zerolinewidth": 2}, "yaxis": {"automargin": true, "gridcolor": "white", "linecolor": "white", "ticks": "", "title": {"standoff": 15}, "zerolinecolor": "white", "zerolinewidth": 2}}}, "title": {"text": "This is a stripplot!"}, "xaxis": {"anchor": "y", "categoryarray": ["setosa", "versicolor", "virginica"], "categoryorder": "array", "domain": [0.0, 1.0], "title": {"text": "species"}}, "yaxis": {"anchor": "x", "domain": [0.0, 1.0], "title": {"text": "sepal_width"}}},
                        {"responsive": true}
                    )
                };
                
            </script>
        </div>

Надеюсь, что вы найдете ее полезной! Удачного построения :)

person Álvaro H.G    schedule 15.07.2021