用桑基圖分析轉(zhuǎn)專業(yè)數(shù)據(jù)
數(shù)據(jù)來源:西南交通大學(xué)教務(wù)網(wǎng)
西南交通大學(xué)2019年本科生轉(zhuǎn)專業(yè)名單公示
2019和2018的數(shù)據(jù)下載后都是pdf格式,使用pdf處理網(wǎng)站 ilovepdf 將pdf轉(zhuǎn)化成excel。
什么是砂赡耄基圖
來自百度百科
桑基圖(Sankey diagram)墓卦,即删氪海基能量分流圖,也叫陕浼簦基能量平衡圖睁本。它是一種特定類型的流程圖,圖中延伸的分支的寬度對應(yīng)數(shù)據(jù)流量的大小忠怖,通常應(yīng)用于能源呢堰、材料成分、金融等數(shù)據(jù)的可視化分析脑又。因1898年Matthew Henry Phineas Riall Sankey繪制的“蒸汽機(jī)的能源效率圖”而聞名暮胧,此后便以其名字命名為“桑基圖”问麸。
所以往衷,桑基圖中分支的寬度對應(yīng)數(shù)據(jù)流量的大小严卖,是展現(xiàn)數(shù)據(jù)流動的利器席舍。
來看一個示例,圖源 驢說蛙語/數(shù)據(jù)可視之美 - 上剩基圖
[圖片上傳失敗...(image-c6f6c4-1641968929253)]
是不是很好看来颤,很厲害,非常的nice稠肘?
那我們用它來展示一下轉(zhuǎn)專業(yè)數(shù)據(jù)福铅。
pyecharts
搜索了一下,python中pyecharts繪圖包能夠?qū)崿F(xiàn)上钜酰基圖的繪制滑黔。
使用pip可以很容易的安裝。
pip install pyecharts
有一點(diǎn)注意的是环揽,pyecharts 分為 v0.5.X 和 v1 兩個大版本略荡,v0.5.X 和 v1 間不兼容。網(wǎng)上的教程有一些是基于v0.5.X的歉胶,因此要注意鑒別汛兜,推薦去官網(wǎng)查看教程。本文中通今,pyecharts的版本是1.8.1粥谬。
數(shù)據(jù)讀取清洗
首先讀取數(shù)據(jù)。
import pandas as pd
data_2019 = pd.read_excel('轉(zhuǎn)專業(yè)2019-已轉(zhuǎn)檔.xlsx',skiprows=1) #第一行是標(biāo)題衡创,skiprows=1跳過第一行
data_2019.head()
out:
看起來還是比較不錯的帝嗡,但是很顯然,地球科學(xué)與環(huán)境工程學(xué)院少了一個院字璃氢。
再看一下2018和2017的數(shù)據(jù)哟玷。
data_2018 = pd.read_excel('轉(zhuǎn)專業(yè)2018-已轉(zhuǎn)檔.xlsx',skiprows=1)
data_2018.head()
out:
data_2017 = pd.read_excel('轉(zhuǎn)專業(yè)2017-已轉(zhuǎn)檔.xls',skiprows=1)
data_2017.head()
out:
首先,將各個數(shù)據(jù)加上年份一也,合并巢寡。
data_2019['年份'] = 2019
data_2018['年份'] = 2018
data_2017['年份'] = 2017
data = pd.concat([data_2019,data_2018,data_2017],axis=0)
我們想以學(xué)院作為單位查看流動情況,那么主要使用到的列就是當(dāng)前學(xué)院椰苟、擬轉(zhuǎn)入學(xué)院抑月。
看一下這兩列的情況。
data['當(dāng)前學(xué)院'].unique()
out:
array(['機(jī)械工程學(xué)院', '材料科學(xué)與工程學(xué)院', '生命科學(xué)與工程學(xué)院', '地球科學(xué)與環(huán)境工程學(xué)', '公共管理與政法學(xué)院',
'經(jīng)濟(jì)管理學(xué)院', '馬克思主義學(xué)院', '土木工程學(xué)院', '力學(xué)與工程學(xué)院', '交通運(yùn)輸與物流學(xué)院', '心理研究與咨詢中心',
'數(shù)學(xué)學(xué)院', '物理科學(xué)與技術(shù)學(xué)院', '電氣工程學(xué)院', '茅以升學(xué)院', '信息科學(xué)與技術(shù)學(xué)院', '建筑與設(shè)計(jì)學(xué)院',
'人文學(xué)院', '外國語學(xué)院', '地球科學(xué)與環(huán)境工程學(xué)\n院', '西南交大-利茲學(xué)院', '地球科學(xué)與環(huán)境工程學(xué)院'],
dtype=object)
有帶\n
的舆蝴,還有學(xué)院少個院的谦絮。
再看下擬轉(zhuǎn)入學(xué)院题诵。
data['擬轉(zhuǎn)入學(xué)院'].unique()
out:
array(['土木工程學(xué)院', '機(jī)械工程學(xué)院', '電氣工程學(xué)院', '信息科學(xué)與技術(shù)學(xué)院', '交通運(yùn)輸與物流學(xué)院', '經(jīng)濟(jì)管理學(xué)院',
'人文學(xué)院', '外國語學(xué)院', '建筑與設(shè)計(jì)學(xué)院', '材料科學(xué)與工程學(xué)院', '力學(xué)與工程學(xué)院', '數(shù)學(xué)學(xué)院',
'物理科學(xué)與技術(shù)學(xué)院', '生命科學(xué)與工程學(xué)院', '地球科學(xué)與環(huán)境工程學(xué)院', '公共管理與政法學(xué)院', '茅以升學(xué)院',
'心理研究與咨詢中心', '信息科學(xué)與技術(shù)學(xué)', '信息科學(xué)與技術(shù)學(xué)\n院', '交通運(yùn)輸與物流學(xué)', '交通運(yùn)輸與物流學(xué)\n院',
'材料科學(xué)與工程學(xué)\n院', '材料科學(xué)與工程學(xué)', '物理科學(xué)與技術(shù)學(xué)\n院', '生命科學(xué)與工程學(xué)', '地球科學(xué)與環(huán)境工',
'公共管理與政法學(xué)', '西南交大-利茲學(xué)院', '心理研究與咨詢中', '心理研究與咨詢中\(zhòng)n心', '馬克思主義學(xué)院'],
dtype=object)
有帶\n
的,中心少了心层皱,學(xué)院少了院性锭,地球科學(xué)與環(huán)境工直接少了程學(xué)院三個字。
處理一下叫胖。
data['當(dāng)前學(xué)院'] = data['當(dāng)前學(xué)院'].str.replace(r"\n",'')
data['當(dāng)前學(xué)院'] = data['當(dāng)前學(xué)院'].str.replace('學(xué)$','學(xué)院')
data['擬轉(zhuǎn)入學(xué)院'] = data['擬轉(zhuǎn)入學(xué)院'].str.replace(r"\n",'')
data['擬轉(zhuǎn)入學(xué)院'] = data['擬轉(zhuǎn)入學(xué)院'].replace("地球科學(xué)與環(huán)境工",'地球科學(xué)與環(huán)境工程學(xué)院')
data['擬轉(zhuǎn)入學(xué)院'] = data['擬轉(zhuǎn)入學(xué)院'].str.replace('學(xué)$','學(xué)院')
data['擬轉(zhuǎn)入學(xué)院'] = data['擬轉(zhuǎn)入學(xué)院'].str.replace('中$','中心')
series
直接replace
是整個的替換草冈,str.replace
是部分匹配替換。
$
的意思是正則匹配從結(jié)尾開始匹配瓮增,當(dāng)以學(xué)為結(jié)尾時(shí)替換為學(xué)院怎棱,當(dāng)以中為結(jié)尾時(shí)替換為中心。
看一下處理后的結(jié)果绷跑。
data['當(dāng)前學(xué)院'].unique()
out:
array(['機(jī)械工程學(xué)院', '材料科學(xué)與工程學(xué)院', '生命科學(xué)與工程學(xué)院', '地球科學(xué)與環(huán)境工程學(xué)院', '公共管理與政法學(xué)院',
'經(jīng)濟(jì)管理學(xué)院', '馬克思主義學(xué)院', '土木工程學(xué)院', '力學(xué)與工程學(xué)院', '交通運(yùn)輸與物流學(xué)院', '心理研究與咨詢中心',
'數(shù)學(xué)學(xué)院', '物理科學(xué)與技術(shù)學(xué)院', '電氣工程學(xué)院', '茅以升學(xué)院', '信息科學(xué)與技術(shù)學(xué)院', '建筑與設(shè)計(jì)學(xué)院',
'人文學(xué)院', '外國語學(xué)院', '西南交大-利茲學(xué)院'], dtype=object)
data['擬轉(zhuǎn)入學(xué)院'].unique()
out:
array(['土木工程學(xué)院', '機(jī)械工程學(xué)院', '電氣工程學(xué)院', '信息科學(xué)與技術(shù)學(xué)院', '交通運(yùn)輸與物流學(xué)院', '經(jīng)濟(jì)管理學(xué)院',
'人文學(xué)院', '外國語學(xué)院', '建筑與設(shè)計(jì)學(xué)院', '材料科學(xué)與工程學(xué)院', '力學(xué)與工程學(xué)院', '數(shù)學(xué)學(xué)院',
'物理科學(xué)與技術(shù)學(xué)院', '生命科學(xué)與工程學(xué)院', '地球科學(xué)與環(huán)境工程學(xué)院', '公共管理與政法學(xué)院', '茅以升學(xué)院',
'心理研究與咨詢中心', '西南交大-利茲學(xué)院', '馬克思主義學(xué)院'], dtype=object)
用集合運(yùn)算看一下差集拳恋,看是不是完全一樣。
set(data['當(dāng)前學(xué)院'].unique()) - set(data['擬轉(zhuǎn)入學(xué)院'].unique())
out:
set()
set(data['擬轉(zhuǎn)入學(xué)院'].unique()) - set(data['當(dāng)前學(xué)院'].unique())
out:
set()
確實(shí)一樣了砸捏。
再加上一個年級诅岩,畢竟轉(zhuǎn)專業(yè)大二的多,大三的少带膜,可以作為一個點(diǎn)來分析吩谦。
data['年級'] = data['學(xué)號'].astype(str).str[:4].astype(int)
這行代碼的意思是將學(xué)號列轉(zhuǎn)為str
取前四位再轉(zhuǎn)為int
,其實(shí)除以1e6
也可以膝藕。小數(shù)據(jù)就不糾結(jié)性能問題了式廷,怎么方便怎么來。
最后看一下數(shù)據(jù)芭挽。
data.head()
out:
繪圖
繪圖呢滑废,首先要學(xué)示例,赏嘧Γ基圖的官方示例在 pyecharts扇涑茫基圖
主要核心在于定義nodes
與links
,
from pyecharts import options as opts
from pyecharts.charts import Sankey
nodes = [
{"name": "category1"},
{"name": "category2"},
{"name": "category3"},
{"name": "category4"},
{"name": "category5"},
{"name": "category6"},
]
links = [
{"source": "category1", "target": "category2", "value": 10},
{"source": "category2", "target": "category3", "value": 15},
{"source": "category3", "target": "category4", "value": 20},
{"source": "category5", "target": "category6", "value": 25},
]
c = (
Sankey()
.add(
"sankey",
nodes,
links,
linestyle_opt=opts.LineStyleOpts(opacity=0.2, curve=0.5, color="source"),
label_opts=opts.LabelOpts(position="right"),
)
.set_global_opts(title_opts=opts.TitleOpts(title="Sankey-基本示例"))
.render("sankey_base.html")
)
nodes
代表點(diǎn)辛馆,有名字俺陋,links
代表線,有來源昙篙、去處和值腊状。最后render
到html
,使用瀏覽器打開就能查看了苔可。
拿2019的數(shù)據(jù)試試手缴挖,看看2019年轉(zhuǎn)專業(yè)的情況如何。
all_data = data
data = all_data[all_data['年份']==2019]
將原來的數(shù)據(jù)用all_data
存起來焚辅。
nodes = []
out_map = {}
in_map = {}
value_counts = data['當(dāng)前學(xué)院'].value_counts()
for name,value in value_counts.items():
nodes.append({'name':f'轉(zhuǎn)出-{name}-{value}'})
out_map[name] = f'轉(zhuǎn)出-{name}-{value}'
value_counts = data['擬轉(zhuǎn)入學(xué)院'].value_counts()
for name,value in value_counts.items():
nodes.append({'name':f'轉(zhuǎn)入-{name}-{value}'})
in_map[name] = f'轉(zhuǎn)入-{name}-{value}'
links = []
out_values = data['當(dāng)前學(xué)院'].value_counts().index
in_values = data['擬轉(zhuǎn)入學(xué)院'].value_counts().index
for i in out_values:
for j in in_values:
counts = data[(data['當(dāng)前學(xué)院']==i) & (data['擬轉(zhuǎn)入學(xué)院']==j)]
if counts.empty:
continue
else:
links.append({'source': out_map[i], 'target': in_map[j], 'value': counts.shape[0]})
在定義節(jié)點(diǎn)名稱的時(shí)候映屋,我希望把轉(zhuǎn)入轉(zhuǎn)出以及對應(yīng)的值也寫到名稱里苟鸯,所以也就需要一個in_map
和out_map
來做映射。
然后生成pic
棚点,沒有直接渲染是因?yàn)槿绻敵龅?code>jupyterlab內(nèi)部倔毙,需要這個對象進(jìn)行再次render
。
pic = (Sankey(init_opts = opts.InitOpts(width='1200px',height='1000px'))
.add('', nodes,links,
pos_left='16%',pos_right='0%',
node_width = 30,node_gap = 20,
linestyle_opt=opts.LineStyleOpts(opacity = 0.4,curve = 0.7,color = 'source',width=10),
label_opts=opts.LabelOpts (position = 'left',font_family='Times New Roman'))
.set_global_opts(
title_opts=opts.TitleOpts(title = '2019年-轉(zhuǎn)專業(yè)',subtitle=' 人數(shù)',pos_left='50%',
title_textstyle_opts=opts.TextStyleOpts(font_size=20,font_family='Times New Roman',font_weight='bold'),
subtitle_textstyle_opts=opts.TextStyleOpts(font_size=16,font_family='Times New Roman',font_weight='normal',color='black'))))
我加了一些參數(shù)乙濒,比如圖片的width
和height
,pos_left
和pos_right
是繪制的圖到邊緣的比例卵蛉,opacity
透明度颁股,curve
彎曲度,color='source'
表明線的顏色根據(jù)source決定傻丝,width
是線的長度甘有,position = 'left'
表明標(biāo)簽在節(jié)點(diǎn)的左邊,還定義了很多字體和TitleOpts
自定義的內(nèi)容葡缰,好的圖片就是慢慢的調(diào)整才會好看亏掀。
使用render
函數(shù)渲染到html
。pyecharts圖的優(yōu)勢之處在于它是交互式的圖標(biāo)泛释,你可以將鼠標(biāo)放在上面查看內(nèi)容滤愕。
從圖中可以看出,轉(zhuǎn)入信院怜校、電氣间影、交運(yùn)的人數(shù)眾多,都有50左右茄茁』瓯幔看來大家都知道學(xué)校的優(yōu)勢專業(yè)在哪。地院轉(zhuǎn)出人數(shù)最多裙顽,達(dá)到了77付燥。
我coding的時(shí)候使用的是jupyterlab
,顯然渲染到jupyterlab
內(nèi)部更友好愈犹,pyecharts也提供了這種方式键科。
最開始需要導(dǎo)入并設(shè)置NOTEBOOK_TYPE
。
from pyecharts.globals import CurrentConfig, NotebookType
CurrentConfig.NOTEBOOK_TYPE = NotebookType.JUPYTER_LAB
然后獲得pic
后漩怎。
pic.load_javascript()
pic.render_notebook()
就可以在jupyterlab
中看到繪制的圖形了萝嘁。
圖片的保存稍麻煩,截圖當(dāng)然不是最好的方式扬卷。在渲染成html
后牙言,可以使用Chrome F12打開Devtools,然后按ctrl+shift+p怪得,輸入capture咱枉,選擇capture full size screen
卑硫,就可以利用Chrome實(shí)現(xiàn)全網(wǎng)頁截圖,不過這個圖片有很多白邊蚕断,還需要進(jìn)行裁剪欢伏。
pyecharts也提供了保存圖片的方式,不過需要安裝selenium
或者phantomjs
等Web自動化工具亿乳,最終實(shí)現(xiàn)的還是模擬網(wǎng)頁截圖硝拧,圖片可能依然有大白邊,因此我沒有采用葛假。
轉(zhuǎn)出比例
在我將圖發(fā)到空間后障陶,有人說轉(zhuǎn)專業(yè)的人數(shù)并不能代表這個學(xué)院的流失率。之前的圖表只是反映了哪些學(xué)院更熱門聊训。
左側(cè)的轉(zhuǎn)出人數(shù)相對來說信息量較小抱究,如果以學(xué)院的人數(shù)為基準(zhǔn)進(jìn)行轉(zhuǎn)出比的計(jì)算,就能夠非常直觀的體現(xiàn)學(xué)院的流失率了带斑。
那就開干鼓寺!
此處感謝馬大佬提供學(xué)院人數(shù)數(shù)據(jù),數(shù)據(jù)來自入學(xué)信息勋磕,略有不準(zhǔn)妈候。
num_of_stu = pd.read_excel('學(xué)院人數(shù).xlsx')
num_of_stu.head()
out:
我*,這么早的都有挂滓,果然是大佬州丹。
2019年轉(zhuǎn)專業(yè)主要是2018級的,將2018級的提出來杂彭。
再看看學(xué)院能不能對應(yīng)起來墓毒。
num_of_stu_2018 = num_of_stu[num_of_stu['年級']==2018]
set(num_of_stu_2018['學(xué)院'].unique()) - set(data['當(dāng)前學(xué)院'].unique())
out:
{'利茲學(xué)院', '國際教育學(xué)院', '少數(shù)民族預(yù)科'}
國際教育學(xué)院和少數(shù)民族預(yù)科不在當(dāng)前學(xué)院當(dāng)中,不用管亲怠,利茲名字有錯誤所计,改一下。
num_of_stu_2018['學(xué)院'].replace("利茲學(xué)院",'西南交大-利茲學(xué)院',inplace=True) #inplace=True团秽,直接更改原數(shù)據(jù)
讓學(xué)院變成index
主胧,易于訪問。
num_of_stu_2018.set_index('學(xué)院',inplace=True)
然后我們只取2018級的轉(zhuǎn)專業(yè)學(xué)生习勤。如果不提出來踪栋,相當(dāng)于2017級和2018級一起的轉(zhuǎn)專業(yè)人數(shù)比2018級人數(shù),數(shù)據(jù)就偏大了图毕。
data = all_data[(all_data['年份']==2019)&(all_data['年級']==2018)] # &在pandas用于條件且判斷夷都,括號必須加
接下來繪圖。
比例按百分比展示取小數(shù)點(diǎn)后2位予颤,links
里的值按轉(zhuǎn)出人數(shù)占當(dāng)前學(xué)院2018級人數(shù)的比例囤官。
nodes = []
out_map = {}
in_map = {}
value_counts = data['當(dāng)前學(xué)院'].value_counts()
for name,value in value_counts.items():
ratio = value/num_of_stu_2018.loc[name,"人數(shù)"]
nodes.append({'name':f'轉(zhuǎn)出-{name}-{ratio:.2%}'})
out_map[name] = f'轉(zhuǎn)出-{name}-{ratio:.2%}'
value_counts = data['擬轉(zhuǎn)入學(xué)院'].value_counts()
for name,value in value_counts.items():
ratio = value/num_of_stu_2018.loc[name,"人數(shù)"]
nodes.append({'name':f'轉(zhuǎn)入-{name}-{ratio:.2%}'})
in_map[name] = f'轉(zhuǎn)入-{name}-{ratio:.2%}'
links = []
out_values = data['當(dāng)前學(xué)院'].value_counts().index
in_values = data['擬轉(zhuǎn)入學(xué)院'].value_counts().index
for i in out_values:
for j in in_values:
counts = data[(data['當(dāng)前學(xué)院']==i) & (data['擬轉(zhuǎn)入學(xué)院']==j)]
if counts.empty:
continue
else:
ratio = counts.shape[0]/num_of_stu_2018.loc[i,"人數(shù)"]
links.append({'source': out_map[i], 'target': in_map[j], 'value': ratio})
pic = (Sankey(init_opts = opts.InitOpts(width='1200px',height='1000px'))
.add('', nodes,links,
pos_left='18%',pos_right='0%',
node_width = 30,node_gap = 20,
linestyle_opt=opts.LineStyleOpts(opacity = 0.4,curve = 0.7,color = 'source',width=10),
label_opts=opts.LabelOpts (position = 'left',font_family='Times New Roman'))
.set_global_opts(
title_opts=opts.TitleOpts(title = ' 2019年-2018級-轉(zhuǎn)專業(yè)',subtitle='轉(zhuǎn)入或轉(zhuǎn)出人數(shù)/當(dāng)前學(xué)院2018級人數(shù)',pos_left='50%',
title_textstyle_opts=opts.TextStyleOpts(font_size=20,font_family='Times New Roman',font_weight='bold'),
subtitle_textstyle_opts=opts.TextStyleOpts(font_size=16,font_family='Times New Roman',font_weight='normal',color='black'))))
pic.load_javascript()
pic.render_notebook()
圖中冬阳,左側(cè)的比例代表轉(zhuǎn)出人數(shù)占當(dāng)前學(xué)院人數(shù)的比例,右邊的比例代表轉(zhuǎn)入人數(shù)比擬轉(zhuǎn)入學(xué)院原人數(shù)党饮。中間線的粗細(xì)肝陪,代表流動人數(shù)占當(dāng)前學(xué)院人數(shù)的比例。
從圖中可以看出刑顺,馬院氯窍、生命學(xué)院和地環(huán)學(xué)院的學(xué)生流失嚴(yán)重,馬院達(dá)到了驚人的26%蹲堂,可怕狼讨。因?yàn)榈丨h(huán)學(xué)院有一部分學(xué)院內(nèi)部轉(zhuǎn)專業(yè)的,所以生命學(xué)院比地環(huán)學(xué)院的實(shí)際流失率要嚴(yán)重一些贯城。啊,快跑霹娄!
將links
里的值按轉(zhuǎn)入人數(shù)占擬轉(zhuǎn)入學(xué)院2018級人數(shù)的比例試一下能犯。
從圖中可以看出,電氣學(xué)院和交運(yùn)學(xué)院都是非常友好的犬耻,很歡迎其他學(xué)院轉(zhuǎn)入踩晶,信息學(xué)院第三,人文學(xué)院第四枕磁。
馬大佬強(qiáng)勢出場
按人數(shù)和按比例各有千秋渡蜻,都有信息量,馬大佬建議我整合起來计济。
如果左側(cè)是轉(zhuǎn)出人數(shù)占比茸苇,右側(cè)是人數(shù),那么一張圖就能展示學(xué)院流失率與學(xué)院喜好沦寂。但是由于值的量綱不同学密,這種操作需要強(qiáng)行修改渲染器,我實(shí)力太菜传藏,搞不了腻暮。
馬大佬決定親自上手,用matlab從頭繪制了一個毯侦。
馬大佬的圖按數(shù)值大小排了序哭靖,兩列節(jié)點(diǎn)的標(biāo)簽在兩邊,在pyecharts中我都沒有找到對應(yīng)的實(shí)現(xiàn)方式侈离。
果然自己從頭繪制才是定制性最強(qiáng)的试幽,給馬大佬鼓掌。
最后
本次只分析了2019年的轉(zhuǎn)專業(yè)數(shù)據(jù)卦碾,后續(xù)分析等待進(jìn)一步進(jìn)行抡草。
你有什么分析建議呢饰及,歡迎留言??。