Условное форматирование в Plotly

Этот вопрос касается того, как выполнять условное форматирование в Plotly.

Экземпляры, где это может понадобиться:

  • Диаграммы разброса, где точки должны быть раскрашены (например, радуга) в зависимости от 2 переменных;
  • Интерактивные диаграммы, цветность которых зависит от значений параметров;
  • Гистограммы, части которых нужно раскрасить по-разному.

Здесь я спрошу конкретно о гистограммах.

Возьмем следующие данные:

data = np.random.normal(size=1000)

Я хочу иметь гистограмму, в которой значения выше 0 помещаются в другой цвет.

Простое решение -

hist1 = go.Histogram(x=data[data<0], 
                    opacity=0.75, 
                    histnorm='density',
                    showlegend=False,
                    )
hist2 = go.Histogram(x=data[data>=0], 
                    opacity=0.75, 
                    histnorm='density',
                    showlegend=False,
                    )
layout = go.Layout(barmode='overlay')
fig = go.Figure(data=[hist1, hist2], layout=layout)
iplot(fig, show_link=False)

Условное форматирование в Plotly

У этого решения есть несколько проблем:

  1. Размеры ячеек по умолчанию для двух гистограмм различаются, что приводит к перекрытию около нуля.
  2. Если я хочу иметь histnorm = 'probability density', полученные графики «нормализуют» каждую из отдельных гистограмм, поэтому они будут выглядеть непропорционально.
  3. Группирование начинается слева для обеих гистограмм, поэтому последняя ячейка может выходить за пределы гистограммы значений ниже нуля.

Есть лучший способ это сделать?


ОБНОВИТЬ

Хорошо, я могу решить (1) и (3) с помощью xbins:

hist1 = go.Histogram(x=data[data>=0], 
                    opacity=0.75, 
                    xbins=dict(
                        start=0,
                        end=4,
                        size=0.12),
                    histnorm='density',
                    showlegend=False,
                    )
hist2 = go.Histogram(x=data[data<0], 
                    opacity=0.75, 
                    xbins=dict(
                        start=-0.12*33,
                        end=0,
                        size=0.12),
                    histnorm='density',
                    showlegend=False,
                    )
layout = go.Layout(barmode='overlay')
fig = go.Figure(data=[hist1, hist2], layout=layout)
iplot(fig, show_link=False)

Условное форматирование в Plotly

Но как мне решить вторую проблему?

Как мое предложение сработало для вас?

vestland 19.03.2021 00:06
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
4
1
1 804
1

Ответы 1

Для...

If I want to have histnorm = 'probability density' the resulting plots "normalize" each of the separate histograms, so they will look disproportionate.

... часть кажется, что вам придется нормализовать всю выборку, прежде чем разбивать ее на две разные гистограммы. Это означает, что вы делаете должен, чтобы создать диаграмма с областями с несколькими цветами под одной кривой. Но предлагаемое решение этой проблемы, к сожалению, похоже, состоит в том, чтобы назначить разные цвета двум трассам с помощью ...

df_pos = df.where(df < 0, 0)
df_neg = df.where(df > 0, 0)

... что, конечно же, возвращает вас туда, где вы находитесь.

Итак, чтобы получить то, что вы хотите, кажется, вам придется освободиться от границ gi.Histogram, сначала разобраться с биннингом и нормализацией, а затем использовать комбинацию диаграмм с областями или гистограммы. Насколько я понимаю, это позаботится обо всех трех пунктах. Вот предложение, как это сделать:

Сюжет:

Код:

# imports
import plotly.graph_objects as go
from plotly.offline import iplot
import pandas as pd
import numpy as np

# theme
import plotly.io as pio
#pio.templates
#pio.templates.default = "plotly_white"
pio.templates.default = "none"

# Some sample data
np.random.seed(123)
x = np.random.normal(0, 1, 1000)

# numpy binning
binned = np.histogram(x, bins=30, density=True)

# retain some info abou the binning
yvals=binned[0]
x_last = binned[1][-1]
xvals=binned[1][:-1]

# organize binned data in a pandas dataframe
df_bin=pd.DataFrame(dict(x=xvals, y=yvals))
df_bin_neg = df.where(df['x'] < 0)
df_bin_pos = df.where(df['x'] > 0)

# set up plotly figure
fig=go.Figure()

# neagtive x
fig.add_trace(go.Scatter(
    x=df_bin_neg['x'],
    y=df_bin_neg['y'],
    name = "negative X",
    hoverinfo='all',
    fill='tozerox',
    #fillcolor='#ff7f0e',
    fillcolor='rgba(255, 103, 0, 0.7)',

    line=dict(color = 'rgba(0, 0, 0, 0)', shape='hvh')
))

# positive x
fig.add_trace(go.Scatter(
    x=df_bin_pos['x'],
    y=df_bin_pos['y'],
    name = "positive X",
    hoverinfo='all',
    fill='tozerox',
    #opacity=0.2,
    #fillcolor='#ff7f0e',
    #fillcolor='#1f77b4',
    fillcolor='rgba(131, 149, 193, 0.9)',
    line=dict(color = 'rgba(0, 0, 0, 0)', shape='hvh')
))

# adjust layout to insure max values are included
ymax = np.max([df_bin_neg['y'].max(), df_bin_neg['y'].max()])
fig.update_layout(yaxis=dict(range=[0,ymax+0.1]))

# adjust layout to match OPs original
fig.update_xaxes(showline=True, linewidth=1, linecolor='black', mirror=False, zeroline=False, showgrid=False)
fig.update_yaxes(showline=False)#, linewidth=2, linecolor='black', mirror=True)

fig.show()

Другие вопросы по теме