我正在构建一个 Dash 应用程序,并尝试利用 Plotly 的
category_order
和 color_discrete_sequence
为数据帧列中的唯一字符串值分配特定颜色。这些参数似乎被描述为获取非数字数据(例如字符串)并按照用户指定的顺序为其分配颜色。然而,代码似乎并不适用,我只得到所有数据点的单色黑点(下图)。希望有人能看到我所缺少的内容,并提供如何使用这些参数或其他过程来获得预期结果的指导。
我的流程如下:
我有一个包含“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)
解决了问题。
最终,我混淆了 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
}
可以说不太优雅且内存密集,因为我们为大型数据集存储额外的属性,但它有效。
根据您的问题,我能够解决我的问题,并将在此作为另一个答案分享。我的答案基于 ploty express scatter mapbox。
为了将您提供的示例作为最小运行演示版本运行,我必须更改一些输入。
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)
使用此代码我可以渲染以下地图: