如何使 geopandas 绘图的 colorbar 与 vmax 无关?

huangapple go评论65阅读模式
英文:

How to make the colorbar of a geopandas plot independent of vmax?

问题

我使用以下代码创建漂亮的世界地图并添加了一些统计数据

```python
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.colors as mplc
from mpl_toolkits.axes_grid1 import make_axes_locatable
import geopandas as gpd

world = gpd.read_file(gpd.datasets.get_path("naturalearth_lowres"))
world = world[world.name != "Antarctica"]

np.random.seed(123)
countries = world['name'].sample(75, replace=True).tolist()

data = pd.DataFrame({'country': countries, 'count': np.random.randint(0, 500, 75)})

merged_data = world.merge(data, left_on='name', right_on="country", how="left")

fig, ax = plt.subplots(figsize=(10, 6))

cmap = mplc.LinearSegmentedColormap.from_list("custom", ["r", "b"])

divider = make_axes_locatable(ax)
cax = divider.append_axes("bottom", size="4%", pad=0, aspect=10)

merged_data.plot(column='count', ax=ax, cax=cax, cmap=cmap, linewidth=0.5, legend=True, edgecolors="k", missing_kwds={'color': 'lightgrey'}, legend_kwds={'label': "人数", 'orientation': "horizontal"}, vmin=0, vmax=3000)

fig = ax.figure
cb_ax = fig.axes[1]
cb_ax.tick_params(labelsize=8.5, size=2.5, direction="in")

ax.set_axis_off()
plt.show()

一切看起来都很好(图1),直到我用其他数据替换 vmax=3000,然后突然间色条变得又长又窄(图2)。

有没有办法使色条独立于 vmax,这样当我用其他值制作其他图表时它就不会改变?我希望它在报告中尽可能相似。

[![在这里输入图片描述][1]][1]
图1:色条外观良好 -- 我想要的所有图表的效果。

[![在这里输入图片描述][2]][2]
图2:色条又长又窄 -- 不是我想要的。


<details>
<summary>英文:</summary>

I use the following code to make nice world map with some statistics:

import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.colors as mplc
from mpl_toolkits.axes_grid1 import make_axes_locatable
import geopandas as gpd

world = gpd.read_file(gpd.datasets.get_path("naturalearth_lowres"))
world = world[world.name != "Antarctica"]

np.random.seed(123)
countries = world['name'].sample(75, replace=True).tolist()

data = pd.DataFrame({'country': countries, 'count': np.random.randint(0, 500, 75)})

merged_data = world.merge(data, left_on='name', right_on="country", how="left")

fig, ax = plt.subplots(figsize=(10, 6))

cmap = mplc.LinearSegmentedColormap.from_list("custom", ["r", "b"])

divider = make_axes_locatable(ax)
cax = divider.append_axes("bottom", size="4%", pad=0, aspect=10)

merged_data.plot(column='count', ax=ax, cax=cax, cmap=cmap, linewidth=0.5, legend=True, edgecolors="k", missing_kwds={'color': 'lightgrey'}, legend_kwds={'label': "Number of people", 'orientation': "horizontal"}, vmin=0, vmax=3000)

fig = ax.figure
cb_ax = fig.axes[1]
cb_ax.tick_params(labelsize=8.5, size=2.5, direction="in")

ax.set_axis_off()
plt.show()


Everything looks good (Figure 1) untill I change `vmax=3000` with other data, and then suddenly the colorbar becomes long and narrow (Figure 2).

Is there a way to make the colorbar independent of `vmax` so that it does not change when I make other plots with other values? I want it to look as similar as possible in my report.

[![enter image description here][1]][1]
Figure 1: Good looking colobar -- what I want for all the figures.

[![enter image description here][2]][2]
Figure 2: Long and narrow colorbar -- not what I want.

---
Edit: Included a `MWE` as suggested by @JohanC.


  [1]: https://i.stack.imgur.com/bO4Km.png
  [2]: https://i.stack.imgur.com/Ztupm.png

</details>


# 答案1
**得分**: 0

我认为我通过计算`cb_width`和`cb_height`,然后将其传递给`aspect_ratio`,然后定义`cax.set_aspect(aspect_ratio)`来解决了这个问题:

```python
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.colors as mplc
from mpl_toolkits.axes_grid1 import make_axes_locatable
import geopandas as gpd

world = gpd.read_file(gpd.datasets.get_path("naturalearth_lowres"))
world = world[world.name != "Antarctica"]

np.random.seed(123)
countries = world['name'].sample(75, replace=True).tolist()

data = pd.DataFrame({'country': countries, 'count': np.random.randint(0, 500, 75)})

merged_data = world.merge(data, left_on='name', right_on="country", how="left")

fig, ax = plt.subplots(figsize=(10, 6))

cmap = mplc.LinearSegmentedColormap.from_list("custom", ["r", "b"])

divider = make_axes_locatable(ax)
cax = divider.append_axes("bottom", size="5%", pad=0, aspect=40)

vmin = 0
vmax = 3000

merged_data.plot(column='count', ax=ax, cax=cax, cmap=cmap, linewidth=0.5, legend=True, edgecolors="k", missing_kwds={'color': 'lightgrey'}, legend_kwds={'label': "Number of people", 'orientation': "horizontal"}, vmin=vmin, vmax=vmax)

fig = ax.figure
cb_ax = fig.axes[1]

cb_width = cb_ax.get_window_extent().width / fig.dpi
cb_height = cb_ax.get_window_extent().height / fig.dpi

aspect_ratio = cb_width / cb_height

cax.set_aspect(aspect_ratio)

cb_ax.tick_params(labelsize=8.5, size=2.5, direction="in")

ax.set_axis_off()
plt.show()
英文:

I think I managed to solve it by calculating the cb_width and cb_height and then pass it to aspect_ratio before defining the cax.set_aspect(aspect_ratio):

import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.colors as mplc
from mpl_toolkits.axes_grid1 import make_axes_locatable
import geopandas as gpd

world = gpd.read_file(gpd.datasets.get_path(&quot;naturalearth_lowres&quot;))
world = world[world.name != &quot;Antarctica&quot;]

np.random.seed(123)
countries = world[&#39;name&#39;].sample(75, replace=True).tolist()

data = pd.DataFrame({&#39;country&#39;: countries, &#39;count&#39;: np.random.randint(0, 500, 75)})

merged_data = world.merge(data, left_on=&#39;name&#39;, right_on=&quot;country&quot;, how=&quot;left&quot;)

fig, ax = plt.subplots(figsize=(10, 6))

cmap = mplc.LinearSegmentedColormap.from_list(&quot;custom&quot;, [&quot;r&quot;, &quot;b&quot;])

divider = make_axes_locatable(ax)
cax = divider.append_axes(&quot;bottom&quot;, size=&quot;5%&quot;, pad=0, aspect=40)

vmin = 0
vmax = 3000

merged_data.plot(column=&#39;count&#39;, ax=ax, cax=cax, cmap=cmap, linewidth=0.5, legend=True, edgecolors=&quot;k&quot;, missing_kwds={&#39;color&#39;: &#39;lightgrey&#39;}, legend_kwds={&#39;label&#39;: &quot;Number of people&quot;, &#39;orientation&#39;: &quot;horizontal&quot;}, vmin=vmin, vmax=vmax)

fig = ax.figure
cb_ax = fig.axes[1]

cb_width = cb_ax.get_window_extent().width / fig.dpi
cb_height = cb_ax.get_window_extent().height / fig.dpi

aspect_ratio = cb_width / cb_height

cax.set_aspect(aspect_ratio)

cb_ax.tick_params(labelsize=8.5, size=2.5, direction=&quot;in&quot;)

ax.set_axis_off()
plt.show()

I am not sure how robust this solution is, so please do not hesitate to comment or make different answers.

huangapple
  • 本文由 发表于 2023年3月10日 00:35:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/75687516.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定