在 Plotly Mapbox scatter 中分配点离散颜色

问题描述 投票:0回答:2

我正在构建一个 Dash 应用程序,并尝试利用 Plotly 的

category_order
color_discrete_sequence
为数据帧列中的唯一字符串值分配特定颜色。这些参数似乎被描述为获取非数字数据(例如字符串)并按照用户指定的顺序为其分配颜色。然而,代码似乎并不适用,我只得到所有数据点的单色黑点(下图)。希望有人能看到我所缺少的内容,并提供如何使用这些参数或其他过程来获得预期结果的指导。

Unintended monochromatic marker color demo

我的流程如下:

我有一个包含“band”、“lat”和“lon”列的数据框,其中 lat 和 lon 将指定散点图箱图中某个点的位置,而 band 则采用一系列值,例如:

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)

此地图需要其颜色与其他图表相对应,因此我想使用

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 颜色。当设置了颜色并且相应列中的值不是数字时,该列中的值将按照category_orders中描述的顺序循环通过color_discrete_sequence来分配颜色,除非颜色值是color_discrete_map中的键。

所以它收到了我想要的颜色顺序

band_colors_list = ['green', 'red', 'blue', 'orange', 'purple', 'gray', 'yellow']

最终的标记字典被编译并分配给 scattermapbox 字典并推送到 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)
python plotly mapbox
2个回答
3
投票

解决了问题。

最终,我混淆了 Plotly Express 中的组件和 Plotly Graph Objects 中的各种散点图组件。虽然

category_order
discrete_color_sequence
驻留在 Plotly Express 散点图中,但它们不在 Plotly 图形对象的 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
}

可以说不太优雅且内存密集,因为我们为大型数据集存储额外的属性,但它有效。


0
投票

根据您的问题,我能够解决我的问题,并将在此作为另一个答案分享。我的答案基于 ploty express scatter mapbox

简短说明

为了将您提供的示例作为最小运行演示版本运行,我必须更改一些输入。

  1. 回调被删除
  2. 纬度和经度值从字符串更改为浮点数
  3. band_categories 输入从字符串列表更改为字典,如文档中所述
  4. category_orders 末尾带有“s”
  5. 作为对我的检查,我更改了以“2m”开头并以“6m”结尾的颜色顺序,以验证我们可以使用参数更改顺序

运行代码片段

import pandas as pd
import dash
import plotly.express as px
from dash import html, dcc

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)

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

band_colors_list = ['green', 'red', 'blue', 'orange', 'purple', 'gray', 'yellow']

fig = px.scatter_mapbox(
    df,
    lat="Lat",
    lon="Lon",
    color=df['band'],
    color_discrete_sequence=band_colors_list,
    mapbox_style="open-street-map",
    category_orders=band_categories
)

app = dash.Dash(__name__)

app.layout = html.Div([
    dcc.Graph(figure=fig)
])

app.run_server(debug=True, use_reloader=False)

结果图

使用此代码我可以渲染以下地图:

© www.soinside.com 2019 - 2024. All rights reserved.