Mapbox 中的多个不透明度和宽度 - Plotly for Python

Multiple opacities and widths in Mapbox - Plotly for Python

这是从前一个问题衍生出来的问题,可以在这里找到

我遇到了一些关于在 Mapbox 地图上为多个轨迹设置多个不透明度的问题,每个不透明度都与数据框的特定值相关联。

在遵循了 Rob 的有用答案(参见上文 link),从而找到了第一个主题的解决方案之后,我发现自己陷入了另一个问题,因为我现在想对他的代码进行一些改动。

虽然我的第一个方法是只改变痕迹的不透明度,但我现在需要 both 不透明度的变化 and 每条轨迹的宽度,对应于特定数据帧上的不同值。

我继续复制粘贴 Rob 回答的第一部分,他在其中声明了一些函数并创建了一个数据框:

import requests
import geopandas as gpd
import plotly.graph_objects as go
import itertools
import numpy as np
import pandas as pd
from pathlib import Path

# get geometry of london underground stations
gdf = gpd.GeoDataFrame.from_features(
    requests.get(
        "https://raw.githubusercontent.com/oobrien/vis/master/tube/data/tfl_stations.json"
    ).json()
)

# limit to zone 1 and stations that have larger number of lines going through them
gdf = gdf.loc[gdf["zone"].isin(["1","2","3","4","5","6"]) & gdf["lines"].apply(len).gt(0)].reset_index(
    drop=True
).rename(columns={"id":"tfl_id", "name":"id"})

# wanna join all valid combinations of stations...
combis = np.array(list(itertools.combinations(gdf.index, 2)))

# generate dataframe of all combinations of stations
gdf_c = (
    gdf.loc[combis[:, 0], ["geometry", "id"]]
    .assign(right=combis[:, 1])
    .merge(gdf.loc[:, ["geometry", "id"]], left_on="right", right_index=True, suffixes=("_start_station","_end_station"))
)


gdf_c["lat_start_station"] = gdf_c["geometry_start_station"].apply(lambda g: g.y)
gdf_c["long_start_station"] = gdf_c["geometry_start_station"].apply(lambda g: g.x)
gdf_c["lat_end_station"] = gdf_c["geometry_end_station"].apply(lambda g: g.y)
gdf_c["long_end_station"] = gdf_c["geometry_end_station"].apply(lambda g: g.x)

gdf_c = gdf_c.drop(
    columns=[
        "geometry_start_station",
        "right",
        "geometry_end_station",
    ]
).assign(number_of_journeys=np.random.randint(1,10**5,len(gdf_c)))

gdf_c
f = Path.cwd().joinpath("SO.csv")
gdf_c.to_csv(f, index=False)

# there's an requirement to start with a CSV even though no sample data has been provided, now we're starting with a CSV
df = pd.read_csv(f)

# makes use of ravel simpler...
df["none"] = None

我的特殊问题从这里开始:当尝试创建两个“循环”(一个用于不透明度,一个用于宽度)时,我认为我可以执行以下操作:

BINS_FOR_OPACITY=10
opacity_a = np.geomspace(0.001,1, BINS_FOR_OPACITY)
BINS_FOR_WIDTH=10
width_a = np.geomspace(1,3, BINS_FOR_WIDTH)

fig = go.Figure()

# Note the double "for" statement that follows

for opacity, d in df.groupby(pd.cut(df["number_of_journeys"], bins=BINS_FOR_OPACITY, labels=opacity_a)):
    for width, d in df.groupby(pd.cut(df["number_of_journeys"], bins=BINS_FOR_WIDTH, labels=width_a)):
        fig.add_traces(
            go.Scattermapbox(
                name=f"{d['number_of_journeys'].mean():.2E}",
                lat=np.ravel(d.loc[:,[c for c in df.columns if "lat" in c or c=="none"]].values),
                lon=np.ravel(d.loc[:,[c for c in df.columns if "long" in c or c=="none"]].values),
                line_width=width
                line_color="blue",
                opacity=opacity,
                mode="lines+markers",
        )
    )

[Rob 的原始答案只有第一个 for 语句,没有第二个语句]

然而,以上显然不起作用,因为它产生的痕迹比它应该做的多得多(我真的无法解释为什么,但我想这可能是因为两个 for 语句)。

我突然想到 pd.cut 部分可能隐藏了某种解决方案,因为我需要 类似 的双切,但不能找到正确的方法。

我还通过以下方式创建了一个 Pandas 系列:

widths = pd.cut(df.["size"], bins=BINS_FOR_WIDTH, labels=width_a)

并迭代该系列,但得到了与以前相同的结果(痕迹过多)。

为了强调和澄清我自己,我不需要只有多个不透明度或多个宽度,但我需要两者[=51] =] 和 同时 ,这让我有些麻烦。

非常感谢任何帮助

  • 假设您使用同一列来定义 widthopacity 您只需要一个循环
  • for width 我认为线性 space 优于几何 space,因此创建了 width_a 如下
  • opacitywidth 将同步移动,因为它按相同数量的 bins 切割同一列

排除数据来源的解决方案

BINS = 10
opacity_a = np.geomspace(0.001, 1, BINS)
width_a = np.linspace(0.1, 2, BINS)
fig = go.Figure()
for (opacity, width), d in df.groupby(
    [
        pd.cut(df["number_of_journeys"], bins=BINS, labels=opacity_a),
        pd.cut(df["number_of_journeys"], bins=BINS, labels=width_a),
    ]
):
    fig.add_traces(
        go.Scattermapbox(
            name=f"{d['number_of_journeys'].mean():.2E}",
            lat=np.ravel(
                d.loc[:, [c for c in df.columns if "lat" in c or c == "none"]].values
            ),
            lon=np.ravel(
                d.loc[:, [c for c in df.columns if "long" in c or c == "none"]].values
            ),
            line_color="blue",
            line_width=width,
            opacity=opacity,
            mode="lines+markers",
        )
    )

fig.update_layout(
    mapbox={
        "style": "carto-positron",
        "center": {"lat": 51.520214996769255, "lon": -0.097792388774743},
        "zoom": 9,
    },
    margin={"l": 0, "r": 0, "t": 0, "b": 0},
)