在保持颜色/改变标记的同时覆盖 NdOverlays
Overlay NdOverlays while keeping color / changing marker
我想要一个不同类型 "asset" 的散点图,每个资产应该具有相同的颜色并在图例中标记。我可以使用 Scatter 的 NdOverlay 来做到这一点。然后我想叠加两个这样的图,例如一个来自模型,另一个来自实验,这样第一个和第二个只改变标记,但每个资产保持相同的颜色。
我希望它能工作
df1 = pd.DataFrame({"asset": ["A", "B", "B"], "x": [1,2,3], "y": [1,2,3]})
df2 = pd.DataFrame({"asset": ["A", "B", "B"], "x": [1.5,2.5,3.5], "y": [1,2,3]})
df1.hvplot.scatter(x="x", y="y", by="asset") * df2.hvplot.scatter(x="x", y="y", by="asset").opts({"Scatter": {"style": {"marker": "d"}}})
但每个资产 df1.hvplot 中的颜色与 df2.hvplot 中的颜色不同。我想要从 df1 和 df2 开始的最简洁的方式。
编辑:是否有一个简单的解决方案,我不必考虑 df1 和 df2 的排序或者它们是否具有完全相同的一组 "assets"。例如,我需要一些可以与
一起使用的东西
df1 = pd.DataFrame({"asset": ["A", "B", "B"], "x": [1,2,3], "y": [1,2,3]})
df2 = pd.DataFrame({"asset": ["C", "B", "A"], "x": [1.5,2.5,3.5], "y": [1,2,3]})
l1=df1.hvplot.scatter(x="x", y="y", by="asset")
l2=df2.hvplot.scatter(x="x", y="y", by="asset").opts(hv.opts.Scatter(marker='d'))
ll=l1*l2
或
df1 = pd.DataFrame({"asset": ["A", "B", "B"], "x": [1,2,3], "y": [1,2,3]})
df2 = pd.DataFrame({"asset": ["A", "B", "B", "C"], "x": [1.5,2.5,3.5, 4], "y": [1,2,3, 1]})
l1=df1.hvplot.scatter(x="x", y="y", by="asset")
l2=df2.hvplot.scatter(x="x", y="y", by="asset").opts(hv.opts.Scatter(marker='d'))
ll=l1*l2
编辑:如果您需要更大的灵活性,有两个选择:
- 设计尺寸容器的样式,以及
- 使用附加值维度进行样式设置。
有关详细信息,请参阅此处:jupyter notebook, github repo,但代码是这样的。
选项 1(更冗长,但如果您在类似 HoloMap 的容器中工作,通常会更容易):
import holoviews as hv
from holoviews import opts, dim
hv.extension('bokeh')
import pandas as pd
import numpy as np
def cycle_kdim_opts(layout, kdim_opts):
"""
For each given kdim of an Nd holoviews container, create an options dict
that can be passed into a holoviews `opts` object.
Parameters
----------
layout : A holoviews Nd container (HoloMap, ...)
kdim_opts : dict of the form {kdim: {style_option: [alternatives]}}
For an example, see below.
"""
# Output shown for:
# kdim_opts = {
# 'h': {'color': ['orange', 'cyan']},
# 'g': {'size': [30, 10]},
# }
values = {kd.name: list(d) for kd, d in zip(layout.kdims, zip(*layout.data.keys()))}
# print(values)
# {'g': ['a', 'b', 'b'], 'h': ['d', 'c', 'd']}
mapping = {}
for kd, o in kdim_opts.items():
unique_values = list(set(values[kd]))
styles = list(o.values())[0]
mapping[kd] = dict(zip(unique_values, styles))
# print(mapping)
# {'h': {'c': 'orange', 'd': 'cyan'}, 'g': {'b': 30, 'a': 10}}
kdim2style = {k: list(v.keys())[0] for k, v in kdim_opts.items()}
# print(kdim2style)
# {'h': 'color', 'g': 'size'}
mapped_styles = {kdim2style[kd]: hv.Cycle([mapping[kd][value] for value in values])
for kd, values in values.items()}
# print(mapped_styles)
# {'size': Cycle(['10', '30', '30']), 'color': Cycle(['cyan', 'orange', 'cyan'])}
return mapped_styles
df1 = pd.DataFrame({'asset': ['A', 'B', 'B'], 'x': [1.,2.,3.], 'y': [1.,2.,3.]})
df2 = pd.DataFrame({'asset': ['A', 'B', 'B', 'C'], 'x': [1.5,2.5,3.5,4], 'y': [1.,2.,3.,1.]})
df = df1.assign(source='exp').merge(df2.assign(source='mod'), how='outer')
labels = hv.Labels(df.assign(l=df.asset+',\n'+df.source), ['x', 'y'], 'l')
l = hv.Dataset(df, ['x', 'y', 'asset', 'source',], []).to(hv.Points).overlay()
od = {
'source': {'size': [30, 10]},
'asset': {'color': ['orange', 'cyan', 'yellow']},
}
options = (
opts.NdOverlay(legend_position='right', show_legend=True, width=500),
opts.Points(padding=.5, show_title=False, title_format='',
toolbar=None, **cycle_kdim_opts(l, od)),
)
l.opts(*options) * labels
选项 2:不那么冗长,但需要更多努力,例如稍后自定义图例。
df1 = pd.DataFrame({'asset': ['A', 'B', 'B'], 'x': [1.,2.,3.], 'y': [1.,2.,3.]})
df2 = pd.DataFrame({'asset': ['A', 'B', 'B', 'C'], 'x': [1.5,2.5,3.5,4], 'y': [1.,2.,3.,1.]})
df = df1.assign(source='exp').merge(df2.assign(source='mod'), how='outer')
labels = hv.Labels(df.assign(l=df.asset+',\n'+df.source), ['x', 'y'], 'l')
l = hv.Points(df, ['x', 'y'], ['asset', 'source',])
options = (
opts.NdOverlay(legend_position='right', show_legend=True, width=500),
opts.Points(padding=.5, show_title=False, show_legend=True,
marker=dim('source').categorize({'exp':'circle', 'mod':'diamond'}),
color=dim('asset').categorize({'A':'orange', 'B':'cyan', 'C':'yellow'}),
size=10, toolbar=None)
)
l.opts(*options) * labels
原始建议(最接近您的示例):
你可以例如使用 hv.Cycle
对象显式设置颜色:
df1 = pd.DataFrame({"asset": ["A", "B", "B"], "x": [1,2,3], "y": [1,2,3]})
df2 = pd.DataFrame({"asset": ["A", "B", "B"], "x": [1.5,2.5,3.5], "y": [1,2,3]})
l1=df1.hvplot.scatter(x="x", y="y", by="asset")
l2=df2.hvplot.scatter(x="x", y="y", by="asset").opts(hv.opts.Scatter(marker='d'))
ll=l1*l2
ll.opts(hv.opts.Scatter(padding=.1, color=hv.Cycle(['blue', 'orange'])))
我想要一个不同类型 "asset" 的散点图,每个资产应该具有相同的颜色并在图例中标记。我可以使用 Scatter 的 NdOverlay 来做到这一点。然后我想叠加两个这样的图,例如一个来自模型,另一个来自实验,这样第一个和第二个只改变标记,但每个资产保持相同的颜色。
我希望它能工作
df1 = pd.DataFrame({"asset": ["A", "B", "B"], "x": [1,2,3], "y": [1,2,3]})
df2 = pd.DataFrame({"asset": ["A", "B", "B"], "x": [1.5,2.5,3.5], "y": [1,2,3]})
df1.hvplot.scatter(x="x", y="y", by="asset") * df2.hvplot.scatter(x="x", y="y", by="asset").opts({"Scatter": {"style": {"marker": "d"}}})
但每个资产 df1.hvplot 中的颜色与 df2.hvplot 中的颜色不同。我想要从 df1 和 df2 开始的最简洁的方式。
编辑:是否有一个简单的解决方案,我不必考虑 df1 和 df2 的排序或者它们是否具有完全相同的一组 "assets"。例如,我需要一些可以与
一起使用的东西df1 = pd.DataFrame({"asset": ["A", "B", "B"], "x": [1,2,3], "y": [1,2,3]})
df2 = pd.DataFrame({"asset": ["C", "B", "A"], "x": [1.5,2.5,3.5], "y": [1,2,3]})
l1=df1.hvplot.scatter(x="x", y="y", by="asset")
l2=df2.hvplot.scatter(x="x", y="y", by="asset").opts(hv.opts.Scatter(marker='d'))
ll=l1*l2
或
df1 = pd.DataFrame({"asset": ["A", "B", "B"], "x": [1,2,3], "y": [1,2,3]})
df2 = pd.DataFrame({"asset": ["A", "B", "B", "C"], "x": [1.5,2.5,3.5, 4], "y": [1,2,3, 1]})
l1=df1.hvplot.scatter(x="x", y="y", by="asset")
l2=df2.hvplot.scatter(x="x", y="y", by="asset").opts(hv.opts.Scatter(marker='d'))
ll=l1*l2
编辑:如果您需要更大的灵活性,有两个选择:
- 设计尺寸容器的样式,以及
- 使用附加值维度进行样式设置。
有关详细信息,请参阅此处:jupyter notebook, github repo,但代码是这样的。
选项 1(更冗长,但如果您在类似 HoloMap 的容器中工作,通常会更容易):
import holoviews as hv
from holoviews import opts, dim
hv.extension('bokeh')
import pandas as pd
import numpy as np
def cycle_kdim_opts(layout, kdim_opts):
"""
For each given kdim of an Nd holoviews container, create an options dict
that can be passed into a holoviews `opts` object.
Parameters
----------
layout : A holoviews Nd container (HoloMap, ...)
kdim_opts : dict of the form {kdim: {style_option: [alternatives]}}
For an example, see below.
"""
# Output shown for:
# kdim_opts = {
# 'h': {'color': ['orange', 'cyan']},
# 'g': {'size': [30, 10]},
# }
values = {kd.name: list(d) for kd, d in zip(layout.kdims, zip(*layout.data.keys()))}
# print(values)
# {'g': ['a', 'b', 'b'], 'h': ['d', 'c', 'd']}
mapping = {}
for kd, o in kdim_opts.items():
unique_values = list(set(values[kd]))
styles = list(o.values())[0]
mapping[kd] = dict(zip(unique_values, styles))
# print(mapping)
# {'h': {'c': 'orange', 'd': 'cyan'}, 'g': {'b': 30, 'a': 10}}
kdim2style = {k: list(v.keys())[0] for k, v in kdim_opts.items()}
# print(kdim2style)
# {'h': 'color', 'g': 'size'}
mapped_styles = {kdim2style[kd]: hv.Cycle([mapping[kd][value] for value in values])
for kd, values in values.items()}
# print(mapped_styles)
# {'size': Cycle(['10', '30', '30']), 'color': Cycle(['cyan', 'orange', 'cyan'])}
return mapped_styles
df1 = pd.DataFrame({'asset': ['A', 'B', 'B'], 'x': [1.,2.,3.], 'y': [1.,2.,3.]})
df2 = pd.DataFrame({'asset': ['A', 'B', 'B', 'C'], 'x': [1.5,2.5,3.5,4], 'y': [1.,2.,3.,1.]})
df = df1.assign(source='exp').merge(df2.assign(source='mod'), how='outer')
labels = hv.Labels(df.assign(l=df.asset+',\n'+df.source), ['x', 'y'], 'l')
l = hv.Dataset(df, ['x', 'y', 'asset', 'source',], []).to(hv.Points).overlay()
od = {
'source': {'size': [30, 10]},
'asset': {'color': ['orange', 'cyan', 'yellow']},
}
options = (
opts.NdOverlay(legend_position='right', show_legend=True, width=500),
opts.Points(padding=.5, show_title=False, title_format='',
toolbar=None, **cycle_kdim_opts(l, od)),
)
l.opts(*options) * labels
选项 2:不那么冗长,但需要更多努力,例如稍后自定义图例。
df1 = pd.DataFrame({'asset': ['A', 'B', 'B'], 'x': [1.,2.,3.], 'y': [1.,2.,3.]})
df2 = pd.DataFrame({'asset': ['A', 'B', 'B', 'C'], 'x': [1.5,2.5,3.5,4], 'y': [1.,2.,3.,1.]})
df = df1.assign(source='exp').merge(df2.assign(source='mod'), how='outer')
labels = hv.Labels(df.assign(l=df.asset+',\n'+df.source), ['x', 'y'], 'l')
l = hv.Points(df, ['x', 'y'], ['asset', 'source',])
options = (
opts.NdOverlay(legend_position='right', show_legend=True, width=500),
opts.Points(padding=.5, show_title=False, show_legend=True,
marker=dim('source').categorize({'exp':'circle', 'mod':'diamond'}),
color=dim('asset').categorize({'A':'orange', 'B':'cyan', 'C':'yellow'}),
size=10, toolbar=None)
)
l.opts(*options) * labels
原始建议(最接近您的示例):
你可以例如使用 hv.Cycle
对象显式设置颜色:
df1 = pd.DataFrame({"asset": ["A", "B", "B"], "x": [1,2,3], "y": [1,2,3]})
df2 = pd.DataFrame({"asset": ["A", "B", "B"], "x": [1.5,2.5,3.5], "y": [1,2,3]})
l1=df1.hvplot.scatter(x="x", y="y", by="asset")
l2=df2.hvplot.scatter(x="x", y="y", by="asset").opts(hv.opts.Scatter(marker='d'))
ll=l1*l2
ll.opts(hv.opts.Scatter(padding=.1, color=hv.Cycle(['blue', 'orange'])))