Условное форматирование в 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)
Есть несколько проблем с этим решением:
- Размеры бина по умолчанию различны для двух гистограмм, что приводит к перекрытию около нуля.
- Если я хочу иметь
histnorm = 'probability density'
результирующие графики "нормализуют" каждую из отдельных гистограмм, поэтому они будут выглядеть непропорционально. - Биннинг начинается слева для обеих гистограмм, поэтому последний бин может выйти за пределы гистограммы значений ниже нуля.
Есть лучший способ сделать это?
ОБНОВИТЬ
Хорошо, я могу решить (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)
Но как мне решить второй вопрос?
0 ответов
Для...
Если я хочу, чтобы histnorm = 'вероятностная плотность', полученные графики "нормализовали" каждую из отдельных гистограмм, поэтому они будут выглядеть непропорционально.
... часть кажется, что вам придется нормализовать всю выборку, прежде чем разбивать ее на две разные гистограммы. Это означает, что вам следует создать диаграмму с несколькими цветами под одной кривой. Но предлагаемое решение этой проблемы, к сожалению, похоже, состоит в том, чтобы назначить разные цвета двум трассам с помощью...
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()