在Plotly贴图散布图中为点分配离散颜色

时间:2020-09-17 20:31:36

标签: python plotly mapbox

我正在构建一个Dash应用程序,并尝试利用Plotly的category_ordercolor_discrete_sequence为驻留在dataframe列中的唯一字符串值分配特定的颜色。这些参数似乎被描述为获取非数字数据(例如字符串)并按照用户指定的顺序为其分配颜色。但是,该代码似乎没有用,我对所有数据点仅获得单色黑点(下图)。希望有人可以看到我所缺少的内容,并提供有关如何使用这些参数或其他过程来获得预期结果的指导。

Unintended monochromatic marker color demo

我的过程如下:

我有一个数据框,其中包含“ band”,“ lat”和“ lon”列,其中lat和lon将指定散点图框图中点的位置,并且band取值列表,例如:< / p>

data = {'band':['10m','10m','6m','2m','2m','2m','1.25m','70cm','33cm','33cm','33cm','23cm'],
              'Lat': ['35.5','34.2','35.9','36.1','35.2','36.2','33.9','36.4','35.1','34.9','32.9','35.0'],
              'Lon': ['-78.5','-77.3','-79.0','-78.6','-79.3','-77.0','-78.5','-77.7','-79.9','-78.8','-79.1','-79.0']}
df = pd.DataFrame.from_dict(data)

此mapplot将需要其颜色与其他图表相对应,因此我想在文档阅读时使用category_order ...

默认情况下,在Python 3.6+中,轴,图例和构面中的分类值的顺序取决于在data_frame中首次遇到这些值的顺序(默认情况下,在3.6以下的Python中无法保证顺序)。此参数用于强制每列值的特定排序。该字典的键应与列名相对应,并且值应为与所需的特定显示顺序相对应的字符串列表。”

band_categories = {'band':['10m', '6m', '2m', '1.25m', '70cm', '33cm', '23cm']}

设置为建立希望散点图框使用的顺序。

最后,color_discrete_sequence根据定义直接与category_order一起使用...

字符串应定义有效的CSS颜色。如果设置了color并且相应列中的值不是数字,则除非color的值是color_discrete_map中的键,否则将按照category_orders中描述的顺序循环通过color_discrete_sequence为该列中的值分配颜色。

它会收到我想要的颜色顺序band_colors_list = ['green', 'red', 'blue', 'orange', 'purple', 'gray', 'yellow']

最终的标记dict被编译并分配给scattermapbox dict,并推送到Dash元素。

import pandas as pd
import numpy as np
from pandas.api.types import CategoricalDtype
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output

data = {'band':['10m','10m','6m','2m','2m','2m','1.25m','70cm','33cm','33cm','33cm','23cm'],
              'Lat': ['35.5','34.2','35.9','36.1','35.2','36.2','33.9','36.4','35.1','34.9','32.9','35.0'],
              'Lon': ['-78.5','-77.3','-79.0','-78.6','-79.3','-77.0','-78.5','-77.7','-79.9','-78.8','-79.1','-79.0']}
df = pd.DataFrame.from_dict(data)
df.head()
token = drop_mapbox_token_string_here
layers = []
band_categories = ['10m', '6m', '2m', '1.25m', '70cm', '33cm', '23cm']
band_colors_list = ['green', 'red', 'blue', 'orange', 'purple', 'gray', 'yellow']
app = dash.Dash(__name__)

template = {'layout': {'paper_bgcolor': "#f3f3f1", 'plot_bgcolor': "#f3f3f1"}}

def blank_fig(height):
    """
    Build blank figure with the requested height
    """
    return {
        'data': [],
        'layout': {
            'height': height,
            'template': template,
            'xaxis': {'visible': False},
            'yaxis': {'visible': False},
        }
    }

# Build Dash layout
app.layout = html.Div(children=[
    html.Div(children=[
        dcc.Graph(
            id='map-graph',
            figure=blank_fig(500),
            config={'displayModeBar': False},
        ),],
        id="map-div"
    )
])

@app.callback(
    Output('map-graph', 'figure'),
    [Input('map-graph', 'relayoutData')])
def update_plots(relayout_data):

    marker = {
        'color': df.band,
        'category_order': band_categories,
        'color_discrete_sequence': band_colors_list,
        'size': 5,
        'opacity': 0.6
    }
    map_graph = {'data': [{
        'type': 'scattermapbox',
        'lat': df.Lat, 'lon': df.Lon,
        'marker': marker
        }],
        'layout': {
            'template': template,
            'uirevision':True,
            'mapbox':{
                'style': "light",
                'accesstoken': token,
                'layers': layers,
            },
            'margin': {"r": 0, "t": 0, "l": 0, "b": 0},
            'height': 500
        },
    }
    map_graph['layout']['mapbox'].update()

    return (map_graph)

app.scripts.config.serve_locally = True
app.css.config.serve_locally = True
app.run_server(debug=True, use_reloader=False)

1 个答案:

答案 0 :(得分:0)

解决了这个问题。

最终,我与Plotly Express中的组件和Plotly Graph Objects中的各种散布图组件混为一谈。 category_orderdiscrete_color_sequence驻留在Plotly Express散点图中,但不在Plotly Graph Object的Scattermapbox中。

尽管一种途径是转换为使用允许离散颜色定义的其他组件之一,但我还是采用了侵入性较小的方法。只需根据band列定义一个具有预设颜色的新数据框列,然后将此列推入color下的marker参数中即可。

band_categories = ['10m', '6m', '2m', '1.25m', '70cm', '33cm', '23cm']
band_colors_list = ['green', 'red', 'blue', 'orange', 'purple', 'gray', 'yellow']
band_dict = dict(zip(band_categories,band_colors_list)) #set up band to color substitution dict
df['color'] = df['band'].replace(to_replace=band_colorscale)

及以后...

marker = {
    'color': df.color,
    'size': 5,
    'opacity': 0.6
}

可以说,它不那么优雅且占用大量内存,因为我们为大型数据集存储了额外的属性,但它确实有效。