世衛(wèi)組織的錢從哪里來到哪里去
世界衛(wèi)生組織(WHO)是有影響力的全球性組織之一,本實(shí)驗(yàn)獲取了 WHO 官網(wǎng)發(fā)布的 2018 - 2019 年財(cái)政預(yù)算數(shù)據(jù),對(duì)其資金來源及使用情況做了分析,研究及可視化了其資金構(gòu)成、資助項(xiàng)目及資金流向等問題。
輸入并執(zhí)行魔法命令 %matplotlib inline 。
%matplotlib inline
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings("ignore") # 屏蔽報(bào)警
數(shù)據(jù)準(zhǔn)備
本數(shù)據(jù)集來自 WHO,WHO 在其官網(wǎng)向全球公開透明其資金來源情況以及援助項(xiàng)目情況。本數(shù)據(jù)集統(tǒng)計(jì)了在 2018 - 2019 年間指定自愿捐款(Specified voluntary contributions)向 WHO 捐助的資金及 WHO 使用其資金的情況。
導(dǎo)入數(shù)據(jù)進(jìn)行初步處理并查看前 5 行。
import pandas as pd
df=pd.read_excel('https://labfile.oss.aliyuncs.com/courses/3023/World_health_organization_clearning.xlsx')
# 添加各地區(qū)代碼對(duì)應(yīng)的地區(qū)全稱
cont_code_name={
'AF':'Africa',
'EM':'Eastern Mediterranean',
'HQ':'Headquarters',
'SE':'South East Asia',
'EU':'Europe',
'WP':'Western Pacific',
'AM':'Americas',
}
df['cont_name']=df['cont'].apply(lambda x:cont_code_name[x])
df.head()
從運(yùn)行結(jié)果可知,數(shù)據(jù)集由 5 個(gè)字段構(gòu)成,各字段解釋見下表:

世衛(wèi)組織的資金構(gòu)成
根據(jù) WHO 官方網(wǎng)站公開披露的 2018 - 2019 兩年預(yù)算顯示,WHO 的資金來源主要分為會(huì)費(fèi)分?jǐn)偅ˋssessed contributions)與接受捐贈(zèng)兩種形式,接受捐贈(zèng)又分為指定自愿捐款(Specified voluntary contributions),核心自愿捐助(Core voluntary contributions),PIP 捐款(PIP Contributions)。
運(yùn)行以下代碼,可視化 WHO 的資金來源結(jié)構(gòu)。
plt.rcParams['figure.figsize'] = (10, 6)
funding_structure = {
'Assessed contributions': 956900000,
'Specified voluntary contributions': 4328057662,
'Core voluntary contributions': 160592410.5,
'PIP Contributions': 178053223,
}
plt.pie(
x=funding_structure.values(),
labels=funding_structure.keys(), # 標(biāo)簽值
explode=[0.1, 0, 0, 0], # 餅圖中每一塊相對(duì)中心的偏移
textprops={'size': 20}, # 餅圖中文本格式的設(shè)置
autopct='%.2f%%' # 文本字符顯示格式
)
plt.title('The Contributor Funding Structure of World Health Organization', size=22)
從運(yùn)行結(jié)果可知,WHO 成員國會(huì)費(fèi)占其總預(yù)算比例相對(duì)較低,只有 17%,剩余部分全部來源于自愿捐款,而本實(shí)驗(yàn)數(shù)據(jù)集涉及的指定自愿捐款(Specified voluntary contributions)占到所有捐款的比例最高,為總預(yù)算的 76.96% 。
資助組織及被資助項(xiàng)目結(jié)構(gòu)
按資金流向分組聚合,并將聚合結(jié)果歸一化后拼接。
import numpy as np
# 篩選出流入 WHO 的資金,按資助組織進(jìn)行資助金額的求和并逆序排序
funding_from = df.loc[df['direction'] == 'from'].groupby(
['orgs'])['money'].sum().sort_values(ascending=False)
# 將資金歸一化處理
funding_from = funding_from/np.sum(funding_from)
# 同樣的方式處理被資助項(xiàng)目
funding_to = df.loc[df['direction'] == 'to'].groupby(
['orgs'])['money'].sum().sort_values(ascending=False)
funding_to = funding_to/np.sum(funding_to)
# 將兩個(gè)Series對(duì)象按列拼接
funding_from_to = pd.concat([funding_from, funding_to])
# 展示處理后的數(shù)據(jù)
funding_from_to
資助組織及被資助項(xiàng)目金額經(jīng)過歸一化后,其求和值均為 1 ,因此將其可視化到餅圖時(shí),每一部分的占比會(huì)被壓縮到一半(因?yàn)轱瀳D的總百分比是 100%,但是兩部分求和結(jié)果是 200%)。例如,美國(United States of America)在所有資助組織中資助的金額占比為 15.17%,但經(jīng)拼接后在 funding_from_to 數(shù)據(jù)并可視化到餅圖后,其占比將縮小至 7.585%,因此為了顯示 15.17% 數(shù)據(jù),需要在 plt.pie 接口參數(shù) autopct 傳入格式化函數(shù) autopct_fun,該函數(shù)將顯示的百分比數(shù)據(jù)擴(kuò)大為原值的 2 倍。
plt.rcParams['figure.figsize'] = (15, 15)
def autopct_fun(x):
return '%.2f%%' % (2*x)
plt.pie(x=funding_from_to,
labels=funding_from_to.index,
textprops={'size': 17},
autopct=autopct_fun)
plt.title(
'Where Does the World Health Organization Contributor Funding From and Go ?', size=28)
上面的圖中因?yàn)轭悇e太多,導(dǎo)致 label 都擠在一起看不清。下面我們對(duì)上面的圖像進(jìn)行簡化。 具體的 index 的編號(hào)可以通過 np.where 來找到。
plt.rcParams['figure.figsize'] = (14, 7)
def autopct_fun(x):
if 2*x < 8:
return None
else:
return '%.2f%%' % (2*x)
plt.pie(x=funding_from_to.values,
labels=list(funding_from_to.index[0:4]) + ['' for i in funding_from_to.index[4:301]] + list(
funding_from_to.index[301:304]) + ['' for i in funding_from_to.index[304:]],
textprops={'size': 13},
autopct=autopct_fun)
plt.title(
'Where Does the World Health Organization Contributor Funding From and Go ?', size=17)
從輸出結(jié)果可以看出:
WHO 最大的捐助國為美國( United States of America ),占比 15.18%,其次是蓋茨基金會(huì)( Bill & Melinda Gates Foundation ),占比 12.12%,排名第三的是全球疫苗和免疫聯(lián)盟( GAVI Alliance ),占比 8.19%;
WHO 最大的投資項(xiàng)目為根除脊髓灰質(zhì)炎( Polio eradication ),占比 26.55%,其次是提升獲得基本保健和營養(yǎng)服務(wù)( Increase access to essential healthand nutrition services ),占比 12.05%,排名第三的是可預(yù)防疾病疫苗( Vaccine-Preventable Diseases ),占比 8.96%。
不同地區(qū)的資助項(xiàng)目
本實(shí)驗(yàn)的目的是繪制兩個(gè)半圓形餅圖,內(nèi)半圓表示各個(gè)被資助地區(qū)的百分占比,外半圓表示各個(gè)地區(qū)對(duì)應(yīng)項(xiàng)目的百分占比,且須做到內(nèi)半圓地區(qū)塊與外半圓項(xiàng)目塊顏色的主色調(diào)完全對(duì)應(yīng)。由于本例相對(duì)復(fù)雜,因此分步驟進(jìn)行繪圖解釋:
生成內(nèi)半圓數(shù)據(jù)集
選擇資金流向?yàn)?to 的數(shù)據(jù),以地區(qū)進(jìn)行資金聚合、逆序排序并歸一化,查看前 5 行數(shù)據(jù)。
funding_cont_to = df.loc[df['direction'] == 'to'].groupby(
['cont'])['money'].sum().sort_values(ascending=False)
funding_cont_to = funding_cont_to/np.sum(funding_cont_to)
funding_cont_to.head()
內(nèi)半圓的顏色變化序列
Matplolib 默認(rèn)顏色序列為 TABLEAU_COLORS,可通過 cs.to_rgba 將其轉(zhuǎn)變?yōu)?rgba 顏色元組(plt.pie 接口需要的顏色參數(shù)類型為 rgba 或者 rgb 格式),內(nèi)半圓仍然采用默認(rèn)序列的顏色。
import matplotlib.colors as cs
start_colors=[cs.to_rgba(value) for value in cs.TABLEAU_COLORS.values()]
start_colors
外半圓的顏色變化序列
外圈的項(xiàng)目顏色塊由其對(duì)應(yīng)的內(nèi)圈的主題顏色確定,例如對(duì)于非洲 AF 地區(qū)的項(xiàng)目,如果 AF 的色塊顏色為藍(lán)色,則非洲區(qū)所有項(xiàng)目顏色均為藍(lán)色,但為了將項(xiàng)目進(jìn)行有效區(qū)分,需要將各項(xiàng)目顏色進(jìn)行漸變色設(shè)置,其原理是進(jìn)行主題色到白色的線性插值。
colors = []
for i, c in enumerate(funding_cont_to.index):
# WHO向每個(gè)大區(qū)投入的項(xiàng)目,按降序排序
funding_projection = df.loc[(df['direction'] == 'to') & (df['cont'] == c), [
'orgs', 'money']].sort_values('money', ascending=False)
# 獲取每個(gè)地區(qū)總項(xiàng)目數(shù)
color_nums = len(funding_projection)
# 每個(gè)地區(qū)對(duì)應(yīng)項(xiàng)目的主題顏色
r, g, b, a = start_colors[i]
# 根據(jù)主題顏色線性地生成由該主題顏色到白色的序列漸變色
for r_new, g_new, b_new in zip(np.linspace(r, 1, color_nums), np.linspace(g, 1, color_nums), np.linspace(b, 1, color_nums)):
colors.append((r_new, g_new, b_new, 1.0))
colors[:5]
外半圓的標(biāo)簽設(shè)置
由于外半圓項(xiàng)目數(shù)量較大,因此每個(gè)地區(qū)只顯示排名前 1 的項(xiàng)目,其余項(xiàng)目不作顯示。
labels = []
for i, c in enumerate(funding_cont_to.index):
funding_projection = df.loc[(df['direction'] == 'to') & (df['cont'] == c), [
'orgs', 'money']].sort_values('money', ascending=False)
show_top = 1
label = np.append(funding_projection['orgs'][:show_top],
['']*(len(funding_projection)-show_top))
labels = np.append(labels, label)
labels[:5]
生成外半圓數(shù)據(jù)集
funding_proj_to = pd.DataFrame()
for i, c in enumerate(funding_cont_to.index):
funding_projection = df.loc[(df['direction'] == 'to') & (df['cont'] == c), [
'orgs', 'money']].sort_values('money', ascending=False)
# 合并所有地區(qū)的數(shù)據(jù)
funding_proj_to = pd.concat([funding_proj_to, funding_projection])
funding_proj_to.set_index(['orgs'], inplace=True)
funding_proj_to = funding_proj_to/np.sum(funding_proj_to)
funding_proj_to.head()
繪圖
半圓圖的繪制原理:當(dāng)傳入 plt.pie 接口參數(shù) x 的求和值小于 1 時(shí),差額部分將形成餅圖的缺口,因此將歸一化的 x 數(shù)據(jù)乘以 0.5 即可獲得半圓,同理,此時(shí)的百分占比被壓縮成原比例的一半,需要通過 autopct_fun 函數(shù)將其顯示比例數(shù)據(jù)作修正。
plt.rcParams['figure.figsize'] = (15, 15)
def autopct_fun(x):
return '%.2f%%' % (2*x)
# 外圈繪制項(xiàng)目
plt.pie(x=0.5*funding_proj_to, # 當(dāng)x求和小于1時(shí),餅狀圖則按比例顯示,剩余部分為白色,此處將數(shù)據(jù)乘以 0.5,繪制結(jié)果為半圓
labels=labels,
radius=1, # 餅圖半徑
wedgeprops={'width': 0.4}, # 餅的徑向?qū)挾? colors=colors, # 每個(gè)色塊的顏色,此處顏色的格式是 rgba 元組
textprops={'size': 17},
autopct='')
# 內(nèi)圈繪制地區(qū)
plt.pie(x=0.5*funding_cont_to,
labels=funding_cont_to.index,
radius=0.45,
pctdistance=0.8, # 餅狀圖中表示 比例數(shù)據(jù)的文本 相對(duì)中心的位置
wedgeprops={'width': 0.3},
colors=cs.TABLEAU_COLORS,
textprops={'size': 17},
autopct=autopct_fun)
plt.ylim(0.5, 1.2)
plt.title('The Funded Zones and The Top 1 Important Funded Programs', size=30)
從輸出結(jié)果可以看出,已實(shí)現(xiàn)了地區(qū)與地區(qū)項(xiàng)目的顏色對(duì)應(yīng),且地區(qū)項(xiàng)目由主題色到白色進(jìn)行漸變顯示。下表列出了每個(gè)地區(qū)排名前 2 的被資助項(xiàng)目:

非洲被資助項(xiàng)目的相對(duì)比例
非洲毫無疑問是 WHO 項(xiàng)目重點(diǎn)投資地區(qū),以該地區(qū)最大的投資項(xiàng)目為對(duì)比對(duì)象,研究其他項(xiàng)目相對(duì)其所占比例。通過數(shù)據(jù)聚合并逆序排序的方式,獲取最大項(xiàng)目的值,以該值為分母進(jìn)行數(shù)據(jù)的歸一化,此處為美觀考慮,將最大項(xiàng)目的弧度設(shè)置為半圓。
funding_to = df.loc[(df['direction'] == 'to') & (df['cont'] == 'AF')].groupby(
['orgs'])['money'].sum().sort_values(ascending=False)
funding_to = funding_to/np.sum(funding_to)
first_large = funding_to[0]
# 計(jì)算各項(xiàng)目相對(duì)最大投資項(xiàng)目的百分比,最大投資項(xiàng)目比例設(shè)定為 0.5,目的是為了繪制半圓
funding_to_related = funding_to/first_large*0.5
funding_to_related.head(10).index[:10]
通過調(diào)節(jié)接口中圓圈的半徑 radius 及寬度 wedgeprops 兩個(gè)參數(shù),將非洲地區(qū)被投資項(xiàng)目逐環(huán)式地向內(nèi)繪制,最外徑為投資額最大的項(xiàng)目,依次向內(nèi),項(xiàng)目投資額逐漸遞減。
width = 0.03
fig, ax = plt.subplots(1, 1, figsize=(15, 15))
for i, projection_percent in enumerate(funding_to_related):
ax.pie(x=projection_percent,
startangle=90,
radius=1.0-i*width,
labels=['a'], # 輸入圖例文本占位符
autopct='',
textprops={'size': 0},
wedgeprops={'width': width}
)
# 修改圖例為各個(gè)項(xiàng)目的項(xiàng)目名稱
h, l = ax.get_legend_handles_labels()
plt.legend(handles=h,
labels=list(funding_to_related.index),
ncol=2,
bbox_to_anchor=(0.55, 0.85),
loc='upper left',
frameon=False,
fontsize=20)
plt.title('Funded Programs of Africa Weights Related to Polio Eradication',
fontsize=30,
ha='left')
運(yùn)行結(jié)果可以看到各個(gè)項(xiàng)目相對(duì)最大項(xiàng)目的比例,可以看出各項(xiàng)目相對(duì)根除骨髓灰質(zhì)炎項(xiàng)目的相對(duì)比例大小,整理后,相對(duì)比例如下表所示:

資助項(xiàng)目的資金規(guī)模分布
根據(jù)被投資項(xiàng)目的項(xiàng)目金額,將項(xiàng)目分成小于 1 千萬,1 千萬- 1 億,大于 1 億三個(gè)等級(jí)(單位:美金)。
funding_programs = df.loc[df['direction'] == 'to'].copy()
# 1M=100萬=1e6
bins = [funding_programs['money'].min(), 1e7, 1e8,
funding_programs['money'].max()]
funding_programs['program_structure'] = pd.cut(funding_programs['money'],
bins=bins,
labels=[
'<10M', '10M-100M', '>100M']
)
funding_programs['money'] = funding_programs['money'] / \
np.sum(funding_programs['money'])
funding_programs.head()
按投資項(xiàng)目金額及所在地區(qū),研究各地區(qū)項(xiàng)目資金結(jié)構(gòu)。
# 設(shè)定隨機(jī)數(shù)種子
np.random.seed(121)
row_names = ['<10M', '10M-100M', '>100M']
col_names = ['AF', 'EM', 'HQ', 'EU', 'SE', 'WP', 'AM']
fig, axs = plt.subplots(
nrows=len(row_names),
ncols=len(col_names),
figsize=(len(row_names)*7, len(col_names)*1))
for i, s in enumerate(row_names):
for j, c in enumerate(col_names):
ax = axs[i, j]
x = funding_programs.loc[(funding_programs['cont'] == c) & (
funding_programs['program_structure'] == s)]['money'].sum()
# 繪制淺色背景
ax.pie([1], radius=0.8, colors=[(0.97, 0.97, 0.97)])
# 繪制深色主體
ax.pie(x=[x],
startangle=90,
radius=1.0,
labels=['%.2f%%' % round(x*100, 2)],
autopct='%.2f%%',
colors=np.random.random(size=(1, 3)), # 隨機(jī)生成一個(gè)顏色元組(rgb元組)
textprops={'size': 0},
)
ax.legend(frameon=False, fontsize=15)
ax.set_title(label='%s : %s' % (c, s), size=15)
fig.suptitle('The Funded Programs Budget Structure of Different Zones', size=20)
從輸出結(jié)果可以看出:
超大金額(大于 1 億)的投資項(xiàng)目集中在 Africa,Eastern Mediterranean 兩個(gè)地區(qū),Europe 等 4 個(gè)地區(qū)沒有該規(guī)模項(xiàng)目;
中等規(guī)模項(xiàng)目集中在 Headquarters 地區(qū)。
被資助項(xiàng)目的資金規(guī)模分布-樹形地圖
!pip install pyecharts==1.7.1
另一種可視化結(jié)構(gòu)的繪圖對(duì)象為 pyecharts 包提供的 TreeMap 類,與傳統(tǒng)的餅圖不同,TreeMap 用一個(gè)個(gè)的方格表示各元素相對(duì)整體的比例。
from pyecharts import options as opts
from pyecharts.charts import TreeMap
funding_to = df.loc[df['direction'] == 'to'].groupby(
['orgs'])['money'].sum().sort_values(ascending=False)
funding_to = funding_to/np.sum(funding_to)
# 生成 TreeMap 數(shù)據(jù),該數(shù)據(jù)為 json 格式
funding_to_treemap = [{'value': value, 'name': name}
for name, value in zip(funding_to.index, funding_to)]
tree = TreeMap()
tree.add("Funded Budget",
funding_to_treemap,
label_opts=opts.LabelOpts(position="inside", font_size=12),
drilldown_icon=" ",
leaf_depth=1)
tree.set_global_opts(
title_opts=opts.TitleOpts(
title="The Funded Programs Budget Structure",),
legend_opts=opts.LegendOpts(
pos_top='5%', textstyle_opts=opts.TextStyleOpts(font_size=15))
)
tree.render_notebook()
從輸出結(jié)果可以看出前 5 類投資項(xiàng)目將近占到了總項(xiàng)目投資額的 60% 以上。
帶鉆取結(jié)構(gòu)的樹形圖
TreeMap 類還提供了數(shù)據(jù)鉆取功能,原理是傳入的數(shù)據(jù)文件增加一個(gè) children 字典項(xiàng),children 的值為需要被鉆取的 TreeMap 數(shù)據(jù),以下代碼生成了帶鉆取功能的數(shù)據(jù)。
數(shù)據(jù)準(zhǔn)備
funding_to = df.loc[df['direction'] == 'to'].groupby(
['cont_name', 'orgs'], as_index=False)['money'].sum()
funding_to['money'] = funding_to['money']/np.sum(funding_to['money'])
funding_to_treemap = [
{'value': funding_to.loc[funding_to['cont_name'] == name]['money'].sum(),
'name': name,
'children':[
{
'value': funding_to.loc[(funding_to['cont_name'] == name) & (funding_to['orgs'] == org)]['money'].sum(),
'name': org,
}
for org in list(set(funding_to.loc[funding_to['cont_name'] == name, 'orgs']))
]
}
for name in list(set(funding_to['cont_name']))
]
繪圖
tree = TreeMap()
tree.add("Funded Budget",
funding_to_treemap,
label_opts=opts.LabelOpts(position="inside", font_size=20),
drilldown_icon=" ",
leaf_depth=1 # 初始化圖形時(shí),顯示第一層(即最高級(jí))
)
tree.set_global_opts(
title_opts=opts.TitleOpts(
title="The Funded Programs Budget Structure of Different Zones",),
legend_opts=opts.LegendOpts(
pos_top='5%', textstyle_opts=opts.TextStyleOpts(font_size=15))
)
tree.render_notebook()
運(yùn)行以上代碼,輸出的是地區(qū)級(jí)的結(jié)構(gòu)圖,當(dāng)鼠標(biāo)點(diǎn)擊其中一塊區(qū)域時(shí),樹形圖向下鉆取該區(qū)域子項(xiàng)目的結(jié)構(gòu)圖,可通過圖最下面的層級(jí)按鈕可以回到上一級(jí)。
被資助項(xiàng)目的資金規(guī)模分布-太陽花圖
同樣的數(shù)據(jù)結(jié)構(gòu)傳入 Sunburst 類,可以獲得帶有鉆取功能的太陽花圖,太陽花圖帶有兩個(gè)餅圖,內(nèi)部餅圖代表父級(jí)的結(jié)構(gòu)組成,點(diǎn)擊任一顏色塊,數(shù)據(jù)向下鉆取到該層級(jí)下子項(xiàng)的餅圖。
from pyecharts.charts import Sunburst
funding_to = df.loc[df['direction'] == 'to'].groupby(
['cont_name', 'orgs'], as_index=False)['money'].sum()
funding_to['money'] = funding_to['money']/np.sum(funding_to['money'])
# 數(shù)據(jù)結(jié)構(gòu)與樹形圖完全一致
funding_to_treemap = [
{'value': funding_to.loc[funding_to['cont_name'] == name]['money'].sum(),
'name': name,
'children':[
{
'value': funding_to.loc[(funding_to['cont_name'] == name) & (funding_to['orgs'] == org)]['money'].sum(),
'name': org,
}
for org in list(set(funding_to.loc[funding_to['cont_name'] == name, 'orgs']))
]
}
for name in list(set(funding_to['cont_name']))
]
sun = Sunburst()
sun.add(
"",
data_pair=funding_to_treemap,
highlight_policy="ancestor",
radius=[0, "95%"],
sort_="null",
label_opts=opts.LabelOpts(is_show=False, position='inside', font_size=5),
levels=[
{ # 外圈設(shè)置
"r0": "65%", # 內(nèi)圈半徑
"r": "95%", # 外圈半徑
"itemStyle": {"borderWidth": 2},
"label": {"rotate": "tangential"},
},
{ # 內(nèi)圈設(shè)置
"r0": "25%",
"r": "45%",
},
],
)
sun.set_global_opts(title_opts=opts.TitleOpts(title="The Funded Programs Budget Structure of Different Zones"))
sun.set_series_opts(label_opts=opts.LabelOpts(formatter=""))
sun.render_notebook()
世衛(wèi)組織的資金流向
餅狀圖是表達(dá)組成關(guān)系或者總分關(guān)系的最佳繪圖對(duì)象,樹形圖和太陽花圖在餅圖的基礎(chǔ)上,增加了交互式地鉆取功能,實(shí)現(xiàn)了不同層級(jí)結(jié)構(gòu)的總分表達(dá)。在之前的實(shí)驗(yàn)中,我們借助這幾類對(duì)象研究了世衛(wèi)組織的資金來源、資助項(xiàng)目,以下我們進(jìn)一步研究其資金的流向。
描述流向的繪圖對(duì)象主要是 桑基圖,?;鶊D早期的應(yīng)用主要是能量分布,后期在金融領(lǐng)域大放異彩,再后來被廣泛應(yīng)用到經(jīng)管領(lǐng)域。?;鶊D將帶有流向特征的節(jié)點(diǎn)通過一定寬度的線連接起來,線的疏密代表任意兩節(jié)點(diǎn)的能量強(qiáng)弱。?;鶊D繪圖的主要難點(diǎn)在于數(shù)據(jù)準(zhǔn)備工作,以下分布展示其過程:
將資金量小于閾值的組織或者項(xiàng)目設(shè)置為其他類別
為減少?;鶊D可視化的節(jié)點(diǎn)數(shù)量,避免圖片過大反而帶來閱讀困難,將所有資助組織和被資助項(xiàng)目金額小于 1000 萬的均設(shè)置成其他資助者(other contributions)或其他項(xiàng)目(other programs)。
data = df.copy()
limit_money = 1e7
data['orgs_adjust'] = data['orgs']
data.loc[(data['direction'] == 'from') &
(data['money'] <= limit_money), 'orgs_adjust'] = 'other contributions'
data.loc[(data['direction'] == 'to') &
(data['money'] <= limit_money), 'orgs_adjust'] = 'other programs'
data.head()
數(shù)據(jù)構(gòu)建
桑基圖接口主要傳入兩個(gè)數(shù)據(jù),一個(gè)為 nodes,一個(gè)為 links。nodes 表示需要可視化的節(jié)點(diǎn),此處 nodes 由各個(gè)組織名稱和各個(gè)地區(qū)名稱構(gòu)成,對(duì)其進(jìn)行去重和拼接;links 為一個(gè)列表,列表中每個(gè)元素為一個(gè)字典,由 source,target,value 三個(gè)鍵構(gòu)成,分別代表起始節(jié)點(diǎn)、結(jié)束節(jié)點(diǎn)和連接兩節(jié)點(diǎn)的能量值,source 和 target 須包含在 nodes 節(jié)點(diǎn)列表中。節(jié)點(diǎn)連接一共分成兩個(gè)部分,出資國家與被資助地區(qū)為資金流入關(guān)系,被資助地區(qū)與被資助項(xiàng)目為資金流出關(guān)系,因此在生成節(jié)點(diǎn)連接數(shù)據(jù)集時(shí)需要考慮 source 和 target 的方向,以下代碼生成了用于桑基圖接口的數(shù)據(jù)。
sankey_value='money'
# 去重后拼接
node_lists = np.append(np.unique(data['orgs_adjust']),(np.unique(data['cont'])))
nodes = [
{
'name': node
}
for node in node_lists
]
links = [
{
"source": s,
"target": t,
"value": v
}
for s, t, v in zip(data.loc[data['direction'] == 'from', 'orgs_adjust'],
data.loc[data['direction'] == 'from', 'cont'],
data.loc[data['direction'] == 'from', sankey_value],
)
]+[
{
"source": s,
"target": t,
"value": v
}
for t, s, v in zip(data.loc[data['direction'] == 'to', 'orgs_adjust'],
data.loc[data['direction'] == 'to', 'cont'],
data.loc[data['direction'] == 'to', sankey_value],
)
]
桑基圖的繪制
?;鶊D的接口調(diào)用方式和大多數(shù) pyecharts 接口類似。運(yùn)行以下代碼,可生成世衛(wèi)組織 2018 -2019 年指定自愿捐款的資金流向圖,該圖細(xì)致地展示了每一筆資金的使用,在 WHO 官網(wǎng)上,也有一張類似的圖,該圖生動(dòng)地公開了 WHO 經(jīng)費(fèi)使用情況,便于讀者了解 WHO 的資金預(yù)算流向情況。
from pyecharts.charts import Sankey
init_opts=opts.InitOpts(width='1000px',height='1000px')
sankey = Sankey(init_opts=init_opts) # 初始化圖片的寬度和高度
sankey.add(
series_name="",
nodes=nodes,
links=links,
node_gap=7, # 節(jié)點(diǎn)間距
label_opts=opts.LabelOpts(position='right',font_size=15), # 設(shè)置節(jié)點(diǎn)文本
itemstyle_opts=opts.ItemStyleOpts(border_width=2), # 設(shè)置節(jié)點(diǎn)寬度
linestyle_opt=opts.LineStyleOpts(color="source", width=2,curve=0.5, opacity=0.5), # 設(shè)置線型
tooltip_opts=opts.TooltipOpts(trigger_on="click") # 設(shè)置交互方式
)
sankey.set_global_opts(
title_opts=opts.TitleOpts(title="The Funding Budget Flows of Specified Voluntary Contributions of WHO in Year 2018 - 2019"),
)
sankey.render_notebook()