Поместите галочки в середине каждого цвета на бетонную панель - PullRequest
0 голосов
/ 04 июля 2018

Мне нужно изменить расположение моих тиков цветовой шкалы, и мне нужна помощь в этом. До сих пор я пробовал несколько вещей, но ни одна из них не работала удовлетворительно.

У меня есть датафрейм следующего типа:

import pandas as pd
import random
country_iso = ['RU', 'FR', 'HU', 'AT', 'US', 'ES', 'DE', 'CH', 'LV', 'LU']
my_randoms=[random.uniform(0.0, 0.3) for _ in range (len(country_iso))]
df = pd.DataFrame({'iso':country_iso, 'values':my_randoms})
df.loc[df.iso.str.contains('US')]= ['US', 0]
df['binned']=pd.cut(df['values'], bins=[-0.01, 0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3], labels=[0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3])
df.binned = pd.to_numeric(df.binned, errors='coerce')
df = df[['iso', 'binned']]

Как видите, это фрейм данных со значениями iso-country в виде одного столбца, а некоторые в качестве второго столбца - сгруппированным значением от 0 до 0,3. У США есть значение 0, и я хочу, чтобы он имел отдельный цвет. Затем я продолжаю создавать базовую карту с шейп-файлом из естественная земля .

from matplotlib import pyplot as plt, colors as clr, cm
from mpl_toolkits.basemap import Basemap
from matplotlib.patches import Polygon
from matplotlib.collections import PatchCollection
from matplotlib.ticker import FuncFormatter    
import numpy as np

shapefile_path = #path to your shapefile

fig, ax = plt.subplots(figsize=(10,20))
m = Basemap(resolution='c', # c, l, i, h, f or None
            projection='mill',
            llcrnrlat=-62, urcrnrlat=85,
            llcrnrlon=-180, urcrnrlon=180)
m.readshapefile(shapefile_path+r'\ne_110m_admin_0_countries', 'areas')
df_poly = pd.DataFrame({
    'shapes': [Polygon(np.array(shape), True) for shape in m.areas],
    'iso': [gs['ISO_A2'] for gs in m.areas_info]
})
df_poly = df_poly.merge(df, on='iso', how='inner', indicator=True)

cmap = clr.LinearSegmentedColormap.from_list('custom blue', ['#d0dfef', '#24466b'], N=7)
cmap._init()
cmap._lut[0,: ] = np.array([200,5,5,200])/255
pc = PatchCollection(df_poly[df_poly[df.columns[1]].notnull()].shapes, zorder=2)
norm = clr.Normalize()
pc.set_facecolor(cmap(norm(df_poly[df_poly[df.columns[1]].notnull()][df.columns[1]].values)))
ax.add_collection(pc)

mapper = cm.ScalarMappable(norm=norm, cmap=cmap)
mapper.set_array(df_poly[df_poly[df.columns[1]].notnull()][df.columns[1]])
clb = plt.colorbar(mapper, shrink=0.19)

Я создаю карту блюза, но меняю первый цвет на красный. Это потому, что я хочу, чтобы страны со значением 0 имели разные цвета. Все работает великолепно, но если вы посмотрите на цветовую шкалу, отметки смещены.

enter image description here

Есть ли шанс, что кто-нибудь знает, как исправить мой код? Большое спасибо!

1 Ответ

0 голосов
/ 05 июля 2018

Придумывая свой пример к основам, вы можете достичь того, чего хотите, расширив диапазон, который вы передаете своему картостроителю. Я думаю, что это зависит от конкретного варианта использования, имеет ли смысл расширять цветовую карту до отрицательных значений. В любом случае, вот полный пример без Basemap и shapefiles, которые не нужны для этой проблемы.

from matplotlib import pyplot as plt, colors as clr, cm
import numpy as np

fig= plt.figure()
ax = plt.subplot2grid((1,20),(0,0), colspan=19)
cax = plt.subplot2grid((1,20),(0,19))

upper = 0.3
lower = 0.0
N = 7

cmap = clr.LinearSegmentedColormap.from_list(
    'custom blue', ['#d0dfef', '#24466b'], N=N
)
cmap._init()
cmap._lut[0,: ] = np.array([200,5,5,200])/255.0
norm = clr.Normalize()

mapper = cm.ScalarMappable(norm=norm, cmap=cmap)

deltac = (upper-lower)/(2*(N-1))

mapper.set_array(np.linspace(lower-deltac,upper+deltac,10)) #<-- the 10 here is pretty arbitrary
clb = fig.colorbar(mapper, shrink=0.19, cax=cax)

plt.show()

У меня были некоторые проблемы с вертикальным расширением цветовой шкалы, поэтому я решил использовать ключевое слово cax. Также обратите внимание, что в Python 2 есть небольшая проблема с целочисленными делениями. Поэтому я изменил деление с /255 на /255.0. Конечный результат выглядит так:

result of above code

Надеюсь, это поможет.

EDIT

Очевидно, что вызов norm() изменяет состояние объекта Normalize. Предоставляя новый объект Normalize конструктору ScalarMappable, код начинает работать так, как задумано. Я все еще сбит с толку, почему это происходит. В любом случае, ниже полного кода для создания графика и цветовой шкалы (обратите внимание, что я изменил размер рисунка и масштабирование цветовой шкалы):

from matplotlib import pyplot as plt, colors as clr, cm
from mpl_toolkits.basemap import Basemap
from matplotlib.patches import Polygon
import pandas as pd
import random
country_iso = ['RU', 'FR', 'HU', 'AT', 'US', 'ES', 'DE', 'CH', 'LV', 'LU']
my_randoms=[random.uniform(0.0, 0.3) for _ in range (len(country_iso))]
df = pd.DataFrame({'iso':country_iso, 'values':my_randoms})
df.loc[df.iso.str.contains('US')]= ['US', 0]
df['binned']=pd.cut(df['values'], bins=[-0.01, 0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3], labels=[0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3])
df.binned = pd.to_numeric(df.binned, errors='coerce')
df = df[['iso', 'binned']]

import numpy as np
from matplotlib.collections import PatchCollection
from matplotlib.ticker import FuncFormatter

shapefile_path = 'shapefiles/'

fig, ax = plt.subplots()#figsize=(10,20))

upper = 0.3
lower = 0.0
N = 7

deltac = (upper-lower)/(2*(N-1))

m = Basemap(resolution='c', # c, l, i, h, f or None
            projection='mill',
            llcrnrlat=-62, urcrnrlat=85,
            llcrnrlon=-180, urcrnrlon=180,
            ax = ax,
            )
m.readshapefile(shapefile_path+r'ne_110m_admin_0_countries', 'areas')
df_poly = pd.DataFrame({
    'shapes': [Polygon(np.array(shape), True) for shape in m.areas],
    'iso': [gs['ISO_A2'] for gs in m.areas_info]
})

df_poly = df_poly.merge(df, on='iso', how='inner', indicator=True)

cmap = clr.LinearSegmentedColormap.from_list('custom blue', ['#d0dfef', '#24466b'], N=N)
cmap._init()
cmap._lut[0,: ] = np.array([200,5,5,200])/255.0

pc = PatchCollection(df_poly[df_poly[df.columns[1]].notnull()].shapes, zorder=2)
norm = clr.Normalize()

pc.set_facecolor(cmap(norm(df_poly[df_poly[df.columns[1]].notnull()][df.columns[1]].values)))
ax.add_collection(pc)

mapper = cm.ScalarMappable(norm=clr.Normalize(), cmap=cmap)
mapper.set_array([lower-deltac,upper+deltac])
clb = plt.colorbar(mapper, shrink=0.55)

plt.show()

Получившийся сюжет выглядит так:

result of the second patch of code

...