ПИТОН. НАУКА ДАННЫХ. ГЕОВИЗУАЛИЗАЦИЯ

Битва при Хороплетах — Часть 3 — Фолиум

Использование пакета Folium для создания потрясающих хороплетов

В последних двух статьях этой серии мы смогли создать хороплеты из пакетов Geopandas и Altair. Давайте продолжим наше обучение, но на этот раз воспользуемся пакетом folium.

ПАКЕТ ФОЛИУМ

В этой статье мы представили пакет folium, а также преимущества и недостатки пакета.

Как мы увидим позже, folium предоставляет множество инструментов настройки, которые помогут сделать нашу карту более интерактивной и профессионально выглядящей. Это происходит, конечно, с дополнительными затратами на несколько дополнительных строк кода.

Без дальнейших церемоний, давайте начнем.

КОДИРОВАНИЕ

Часть кодирования аналогична предыдущим двум статьям, но в этой нам не нужно объединять набор данных (за исключением случаев, когда мы хотим создать собственную цветовую карту, как мы увидим позже).

ПОДГОТОВКА

import pandas as pd
import numpy as np
import geopandas as gpd
import folium

ЗАГРУЗКА И ПРЕДВАРИТЕЛЬНАЯ ОБРАБОТКА ДАННЫХ

df = pd.read_csv('data/gdp_per_capita.csv', 
            skiprows=4)
df = df.loc[:,['Country Name','Country Code', '2020']] #Choose only 2020
df = df.fillna(0) #New step 
df.head()

ЗАГРУЗИТЬ ШЕЙПФАЙЛ

gdf = gpd.read_file('shapefiles/world-administrative-boundaries/world-administrative-boundaries.shp')
gdf.head()

ПЛАНИРОВАНИЕ

НАЧАТЬ КАРТУ

#Folium
mymap = folium.Map(location=[19.0649070739746, 73.1308670043945], zoom_start=2,tiles=None)
#ADDING TILES AS A SEPARATE LAYER ALLOWS FOR MORE CONTROL AND MORE SUTOMIZATION
folium.TileLayer('CartoDB positron',name="Light Map",control=False).add_to(mymap)
#Calling the map
mymap

СОЗДАНИЕ ХОРОПЛЕТА ДЛЯ ДОБАВЛЕНИЯ В КАЧЕСТВЕ СЛОЯ

choropleth = folium.Choropleth(
    geo_data=gdf, #the dataset merged with the geometry and the data we need to plot on,
    data=df,
    key_on='feature.properties.name',
    columns=['Country Name', '2020'], #the first one is the 'index' which needs to be connected with the 'key_on' property of  the geo_data
    name = 'GDP per capita (Constant USD 2015 )',
    fill_color='YlGn',
        fill_opacity=0.7,
    line_opacity=0.5,
).add_to(mymap)
mymap

Давайте обсудим параметры кода:

  • geo_data — набор данных шейп-файла
  • data — содержит значения, которые являются основой для цветов картограммы.
  • key_on — это относится к столбцу, найденному в шейп-файле (или параметру geo_data), который будет использоваться для объединения значений со значением параметра data. Причина, по которой мы пропустили часть данных слияния, заключается в следующем: folium выполнит слияние, используя этот параметр.
  • .add_to — чтобы добавить слой картограммы на нашу инициализированную карту.

Одна потенциальная слабость листа Choropleth заключается в том, что он имеет ограниченную цветовую палитру. Пока мы использовали желто-зеленый, но есть способ настроить его позже.

НАСТРОЙКИ

Давайте настроим нашу хороплет, выполнив две вещи:

  1. Добавить всплывающую подсказку для интерактивности
  2. Создайте собственную палитру — красно-желто-зеленую.

ДОБАВИТЬ ПОДСКАЗКУ

Чтобы добавить настраиваемую всплывающую подсказку, нам нужно вызвать класс GeoJsonTooltip фолиума. Это дает нам возможность взаимодействовать и просматривать наши данные, что особенно удобно, когда мы делаем некоторые отчеты.

# Display Region Label
choropleth.geojson.add_child(
    folium.features.GeoJsonTooltip(['name'], labels=True)
)
mymap

СОЗДАТЬ ПОЛЬЗОВАТЕЛЬСКУЮ КАРТУ ЦВЕТОВ

Чтобы попытаться воспроизвести цветовую схему первой серии, необходимо создать пользовательскую карту цветов. Как упоминалось ранее, цветовая палитра, доступная в folium, ограничена, и для создания индивидуальной палитры нам нужно добавить больше строк кода.

В нашей оригинальной статье мы использовали цветовую карту «Красный-Желто-Зеленый». Чтобы использовать это здесь, нам нужно сделать несколько вещей:

1. Создать пользовательскую палитру с помощью библиотеки branca. Цветовая карта, доступная в Folium, ограничена, и если мы хотим создать собственную, нам нужно использовать библиотеку branca.
2. Объединить данные с геоданными. Теперь это стало необходимо, потому что для добавления слоя, где цвет зависит от значения, должны присутствовать данные геометрии.
3. Добавьте отдельный слой с настраиваемой картой цветов. .

import branca
import branca.colormap as cm
#First determine your maximum and minimum values - this becomes the basis for the steps in the map
vmax = df['2020'].max()
vmin = df['2020'].min()
colormap = cm.LinearColormap(colors=['red', 'yellow','green'], 
                             vmin=vmin,
                             vmax=vmax)

ОБЪЕДИНИТЕ ДАННЫЕ

merged = gdf.merge(df, left_on='name', right_on='Country Name' )
merged.head()

ДОБАВИТЬ КАК ОТДЕЛЬНЫЙ СЛОЙ

# First create a function that would call on these values
style_function = lambda x: {"weight":0.5, 
                            'color':'black',
                            'fillColor':colormap(x['properties']['2020']), 
                            'fillOpacity':0.75}
#Add the colormap as a legend
colormap.add_to(mymap)
#Save it as an object so you can add it
folium.features.GeoJson(
        merged.fillna(0),
        style_function=style_function,
    ).add_to(mymap)
mymap

Теперь она напоминает оригинальную карту Альтаира, которую мы создали. Ура!

Чтобы сделать цвет более равномерным, то есть игнорировать выбросы, как мы сделали, используя метод квантилей, нам нужно добавить больше кодов.

КОЛИЧЕСТВЕННЫЙ ПОДХОД — РЕПЛИКАЦИЯ УЧАСТКОВ ИЗ ПРЕДЫДУЩИХ СТАТЕЙ

Чтобы полностью воспроизвести карты, которые мы создали в первых двух статьях, нам нужно убедиться, что значения разделены на несколько шагов (игнорируя выбросы, для которых было сделано предостережение в последней части статьи).

Обратите внимание, однако, что по мере того, как вы постоянно добавляете слои на карту, она загромождается, поэтому может быть проще просто начать новую. Но если вы планируете создать настраиваемую карту цветов, может быть проще не содержать часть картограммы в чистоте следующим образом:

# My Map
#Folium
mymap = folium.Map(location=[19.0649070739746, 73.1308670043945], zoom_start=2,tiles=None)
#ADDING TILES AS A SEPARATE LAYER ALLOWS FOR MORE CONTROL AND MORE SUTOMIZATION
folium.TileLayer('CartoDB positron',name="Light Map",control=False).add_to(mymap)
choropleth = folium.Choropleth(
    geo_data=gdf,
name = 'GDP per capita (Constant USD 2015 )',
).add_to(mymap)
mymap

В приведенном ниже коде показаны предлагаемые нами шаги:

  1. Мы используем индекс (который вручную диктует точки разделения цветов. Для этого сначала требуется, чтобы мы знали максимальное и минимальное значения ряда.
  2. Мы линеаризовали палитру, чтобы значения могли попадать в ячейки в индексе.
  3. Как видите, мы дважды повторили шестнадцатеричный код #c7f6b6. Это не случайно. Похоже, у Folium возникают проблемы с вырезанием последнего цвета в списке, поэтому добавление большего количества цветов обеспечивает более линейный переход перед последним зеленым цветом. Конечно, этот цвет должен быть градиентом последнего, чтобы шестнадцатеричный код был светло-зеленым.
  4. Мы вырезали указатель вручную, и в исходном было использовано всего пять разрезов. Однако для этой карты кажется, что 7 лучше (Напомним, что при индексации диапазона чисел последнее значение все равно игнорируется в Python).
#First determine your maximum and minimum values - this becomes the basis for the steps in the map
vmax = df['2020'].max()
vmin = df['2020'].min()
colormap = cm.LinearColormap(colors=['red','yellow' , '#c7f6b6' ,'#c7f6b6','green'], 
                             vmin=vmin,
                             vmax=vmax)
# We add this in extra step to emulate the coloring scheme of the prveious articles
n_steps = 7 # Quantiles
list_of_values = df['2020'].sort_values()
#Remove all 0's as the geopandas version did not count 0 on the count of merging
list_of_values = [i for i in list_of_values if i != 0]
length = len(list_of_values)
index = [list_of_values[int((length/n_steps)*i)] for i in range(n_steps)]
print(index)
colormap = colormap.to_step(index=index)

Последние штрихи:

Обратите внимание, что в этом случае всплывающую подсказку нужно было добавить как часть последнего слоя, чтобы ее можно было использовать, поэтому мы включили ее в последний использованный слой.

# First create a function that would call on these values
style_function = lambda x: {"weight":0.5, 
                            'color':'black',
                            'fillColor':colormap(x['properties']['2020']), 
                            'fillOpacity':0.75}
#Add the colormap as a legend
colormap.add_to(mymap)
#Save it as an object so you can add it
folium.features.GeoJson(
        merged,
        style_function=style_function,
    tooltip=folium.features.GeoJsonTooltip(['name'], labels=True)
    ).add_to(mymap)
mymap

Вуаля! Мы смогли точно воспроизвести карту, которая была у нас в первых двух статьях, хотя и не без труда.

ЗАКЛЮЧИТЕЛЬНЫЕ ЗАМЕЧАНИЯ

Как мы видели на примере фолиума, возможность настройки достигается за счет удобства. Чтобы воссоздать всю карту из первых двух статей, которые мы создали, нам потребовалось добавить дополнительный код.

Эта стоимость может быть оправдана для тех, кто намеревается развернуть карту, так как выходные данные фолиума можно сохранить в формате HTML. Кроме того, другие функции, такие как возможность добавления настраиваемой всплывающей подсказки, создание визуального радиуса вокруг определенных областей и многие другие, делают folium хорошим пакетом для картограмм.

Полный код на моей странице Github.

ДРУГИЕ СООТВЕТСТВУЮЩИЕ СТАТЬИ

Битва при Хороплетах — Часть 1

Битва при Хороплетах — Часть 2 — Альтаир

Битва за интерактивную географическую визуализацию, часть 1 — интерактивный географик с использованием одной линии…

Битва за интерактивную географическую визуализацию. Часть 2. Интерактивный географик с использованием одной линии…

Битва за интерактивную географическую визуализацию. Часть 3. Графические объекты (Go)

Битва за интерактивную географическую визуализацию. Часть 4 — Альтаир

Битва за интерактивную географическую визуализацию. Часть 5 — Фолиум

Битва за интерактивную географическую визуализацию. Часть 6 — Greppo

Битва за интерактивную географическую визуализацию. Часть 7 — Боке