🔵
pydeck IconLayerに指定するpandas.DataFrameの警告を防ぐ
環境
Python 3.11.1
requirements.txt
pandas==1.5.3
pydeck==0.8.0
問題の概要
pydeck (deck.gl のPythonラッパー) におけるIconLayerの用例を示します。サンプルコードをそのまま引用します。
"""
IconLayer
=========
Location of biergartens in Germany listed on OpenStreetMap as of early 2020.
"""
import pydeck as pdk
import pandas as pd
# Data from OpenStreetMap, accessed via osmpy
DATA_URL = "https://raw.githubusercontent.com/ajduberstein/geo_datasets/master/biergartens.json"
ICON_URL = "https://upload.wikimedia.org/wikipedia/commons/c/c4/Projet_bi%C3%A8re_logo_v2.png"
icon_data = {
# Icon from Wikimedia, used the Creative Commons Attribution-Share Alike 3.0
# Unported, 2.5 Generic, 2.0 Generic and 1.0 Generic licenses
"url": ICON_URL,
"width": 242,
"height": 242,
"anchorY": 242,
}
data = pd.read_json(DATA_URL)
data["icon_data"] = None
for i in data.index:
# ここで警告 (筆者注)
data["icon_data"][i] = icon_data
view_state = pdk.data_utils.compute_view(data[["lon", "lat"]], 0.1)
icon_layer = pdk.Layer(
type="IconLayer",
data=data,
get_icon="icon_data",
get_size=4,
size_scale=15,
get_position=["lon", "lat"],
pickable=True,
)
r = pdk.Deck(layers=[icon_layer], initial_view_state=view_state, tooltip={"text": "{tags}"})
r.to_html("icon_layer.html")
これを実行すると以下のような警告が出ます。警告メッセージにもある通り、上記サンプルコード中でコメントした箇所が問題です。
C:\Foo\main.py:32: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
data['icon_data'][i] = icon_data
pandas.DataFrame
の各行に、表示させたいアイコンの情報をdictオブジェクトとして設定する仕様です。
修正方法
とりあえず以下のようにすれば回避できました。
data["icon_data"] = None
for i in data.index:
data.iat[i, -1] = icon_data
icon_data
の列番号を厳密に指定したければこのように。
data["icon_data"] = None
icon_data_col = data.columns.get_loc("icon_data")
for i in data.index:
data.iat[i, icon_data_col] = icon_data
うまくいかなかった方法
pandasでforループを回すのは大概負けですので、他にスッキリした書き方が無いものかと思っているのですが、今のところ見つけられていません・・・。
pydeck IconLayerの仕様上、dictオブジェクト icon_data
をそのままに要素として格納しなければならないようで、それがpandasの要素・列Series代入の仕様も相まってうまくいかない要因になっている気がします。
ダメだった案を列挙:
data.loc[:, "icon_data"] = icon_data
data.iloc[:, -1] = icon_data
data["icon_data"] = json.dumps(icon_data)
Discussion