简体   繁体   English

plotly.figure_factory.create_annotated_heatmap 未正确显示带有轴标签的图形

[英]plotly.figure_factory.create_annotated_heatmap doesn't show figure with axis labels correctly

I want to show annontated heatmaps in a Plotly Dash app with annotations.我想在带有注释的 Plotly Dash 应用程序中显示带注释的热图。 The heatmap works totally fine, if I didn't add axis labels or if the labels weren't just string with only digits but if I added the axis labels, the figure is too small and the annotations aren't showen correctly.如果我没有添加轴标签,或者标签不仅仅是只有数字的字符串,但如果我添加了轴标签,则热图工作得非常好,数字太小并且注释显示不正确。 如果我在标签上添加一个字符串,它会起作用

带有我真正想要显示的标签

I created a simple example in Dash to illustrate my problem.我在 Dash 中创建了一个简单的示例来说明我的问题。

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.express as px
import plotly.graph_objects as go
import plotly.figure_factory as ff
import numpy as np

import pandas as pd

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv')
app.layout = html.Div([
    dcc.Graph(id='graph-with-slider'),
    dcc.Slider(
        id='year-slider',
        min=df['year'].min(),
        max=df['year'].max(),
        value=df['year'].min(),
        marks={str(year): str(year) for year in df['year'].unique()},
        step=None
    )
])


@app.callback(
    Output('graph-with-slider', 'figure'),
    Input('year-slider', 'value'))
def update_figure(selected_year):
    y = ['8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18']    
    x = ["023", "034", "045", "056", "067", "078"]
    
    
    z = [[4, 4, 2, 1, 0, 0],
        [0, 0, 0, 0, 0, 0], 
        [11, 2, 4, 0, 0, 1],
        [np.nan, 0, 0, 0, 0, 0], 
        [8, 1, 6, 1, 32, 3], 
        [5, 0, 0, 5, 0, 0],
        [0, 0, 0, 0, 0, 0],
        [24, 2, 15, 1, 0, 5],
        [0, 0, 0, 0, 0, 0], 
        [0, 0, 8, 0, 7, 0],
        [0, 0, 0, 9, 0, 0]]
        
    ## it will work if i enabaled the next two lines of code
    #  or if axis labels where just numbers and not something like "032"
    
    
    #x=["add any non numerical string and it will work" + s  for s in x]
    #y=["add any non numerical string and it will work" + s for s in y]
    

    #fig = go.Figure(data=go.Heatmap(z=z))
    
    fig =ff.create_annotated_heatmap(z=z,x=x,y=y, colorscale  = ["green", "yellow", "orange", "red"], showscale = True)
    
    layout = go.Layout(width=500, height=500,
            hovermode='closest',
            autosize=True,
            xaxis=dict(zeroline=False),
            yaxis=dict(zeroline=False, autorange='reversed')
         )
    fig = go.Figure(data=fig, layout=layout)
  
    
    
    return fig


if __name__ == '__main__':
    app.run_server(debug=True)
    
    
    ```

I couldn't replicate your exact issue but I have seen similar我无法复制您的确切问题,但我见过类似的

Try: fig.update_xaxes(type='category')试试: fig.update_xaxes(type='category')

Plotly does some work in the background if it thinks it can force your axis to be 'linear'.如果 Plotly 认为它可以强制您的轴为“线性”,它会在后台执行一些工作。 The type label may avoid this. label 类型可以避免这种情况。

Some background here Plotly Categorical Axes这里有一些背景Plotly 分类轴

It's a bug.这是一个错误。 The problem is in the '_AnnotatedHeatmap' class (pretty sure) operating behind the scenes.问题出在幕后操作的“_AnnotatedHeatmap”class(非常肯定)中。 The 'create_annotated_heatmap' function is not a very smart function. 'create_annotated_heatmap' function 不是一个非常智能的 function。 If you look at the code, it's surprisingly straightforward (seemed to me like more of a quick and dirty solution than the thoroughly developed streamlined plotly stuff).如果您查看代码,它出奇地简单(在我看来,它更像是一个快速而肮脏的解决方案,而不是彻底开发的流线型 plotly 东西)。 I didn't bother trying to work it out, just worked around.我没有费心去解决它,只是解决了问题。 Here's my solution:这是我的解决方案:

fig = ff.create_annotated_heatmap(z=z,
                                  colorscale='greens')
fig.update_layout(overwrite=True,
                  xaxis=dict(ticks="", dtick=1, side="top", gridcolor="rgb(0, 0, 0)", tickvals=list(range(len(x))), ticktext=x)
                  yaxis=dict(ticks="", dtick=1, ticksuffix="   ", tickvals=list(range(len(y))), ticktext=y))
fig.show()

The problem with this work around is that the built-in data-to-axis mapping doesn't happen.这种解决方法的问题是内置的数据到轴的映射不会发生。 You have to map the data yourself.您必须自己 map 数据。 Make sure you sort your axes and have your data sorted that way too!!确保您对轴进行排序,并且也以这种方式对数据进行排序!! I haven't verified that it maps the data right, but I think it does.我尚未验证它是否正确映射了数据,但我认为确实如此。 To ease this issue, here's the code I wrote for this:为了缓解这个问题,这是我为此编写的代码:

x = list(sorted(df['X'].unique().tolist()))
y = list(sorted(df['Y'].unique().tolist()))

z = list()
iter_list = list()
for y_item in y:
    iter_list.clear()
    for x_item in x:
        z_data_point = df[(df['X'] == x_item) & (df['Y'] == y_item)]['Z']
        iter_list.append(0 if len(z_data_point) == 0 else z_data_point.iloc[0])
    z.append([_ for _ in iter_list])

clear as mud?清如泥? Hope I typed that out right.希望我输入正确。 If not, please let me know in the comments.如果没有,请在评论中告诉我。 I'm sure this last bit of code could be improved so if you know how, feel free to leave it in a comment and I'll update it.我确信这最后一点代码可以改进,所以如果你知道如何,请随时将它留在评论中,我会更新它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM