一、問題背景:
一個(gè)集團(tuán)公司下設(shè)有多層企業(yè)镜盯,整體的股權(quán)結(jié)構(gòu)呈垂直特征,即A公司控制B公司猖败、B公司控制C公司速缆,依次向下,現(xiàn)在想獲取每個(gè)公司在這個(gè)垂直結(jié)構(gòu)中的股權(quán)層級(jí)辙浑,如果是頂層的A公司激涤,就是1級(jí),層級(jí)越低判呕,級(jí)數(shù)越大倦踢,這里約定:假如一個(gè)公司G即被母公司A直接持股,也被A公司的子公司B公司直接持股侠草,則G的層級(jí)取2辱挥,而非3。案例測(cè)試數(shù)據(jù)為:
形象的說边涕,就是對(duì)于每個(gè)子公司晤碘,找到最短的“回A之路”。你可以先不看下面的代碼功蜓,想一想應(yīng)該怎么解決這個(gè)問題园爷。
二、代碼實(shí)現(xiàn)如下:
a=Ctrsht['子公司'].unique().tolist()#數(shù)組a記錄了去除所有的非重復(fù)機(jī)構(gòu)
b=[]#數(shù)組b用于記錄每個(gè)機(jī)構(gòu)的層級(jí)
def levelsearch(level,name):
if name=='A':#遞歸函數(shù)必須由明確的退出機(jī)制式撼,否則就是死循環(huán)
return(level,name)#當(dāng)傳入的機(jī)構(gòu)名稱為A時(shí)童社,表示子公司順著股權(quán)路徑找到了A公司,這時(shí)level就是返回的級(jí)數(shù)
else:#如果尚未返回A公司著隆,則記錄此時(shí)尋找到的母公司是哪一個(gè)
for i in range(len(Ctrsht['子公司'])):#遍歷上面的dataframe
if Ctrsht.iloc[i,1]==name:#定位當(dāng)前傳入的機(jī)構(gòu)名稱的所在行
if Ctrsht.iloc[i,0]=='A':#如果該行的母公司即為A則返回層數(shù)
return(level+1,'A')
else:
return(levelsearch(level+1,Ctrsht.iloc[i,0]))#如果該行的母公司不是A扰楼,則調(diào)用函數(shù)本身,下一次傳入的機(jī)構(gòu)名稱即該行的母公司
else:
pass
for i in range(len(a)):#遍歷子公司數(shù)組美浦,計(jì)算每個(gè)子公司的層級(jí)
b.append(levelsearch(1,a[i])[0])
equitylevel=pd.DataFrame(a)
equitylevel['股權(quán)層級(jí)']=b
equitylevel
三弦赖、改進(jìn):
上述程序有個(gè)問題:有的子公司出現(xiàn)了多次,比如F公司浦辨,程序返回層級(jí)為F第一次出現(xiàn)時(shí)的股權(quán)層級(jí)蹬竖,即為3(A--B--F),而如果源數(shù)據(jù)文件中將第6行E->F放在B->F前面,則程序返回的F層級(jí)就是6(A--B--C--D--E--F)案腺。
因此當(dāng)前的結(jié)果依賴于股權(quán)結(jié)構(gòu)表的排序庆冕,需要進(jìn)一步完善。
完善步驟為:
1劈榨、找到某公司在“子公司”列中出現(xiàn)的所有行數(shù)(F公司就是第5行和第6行)
2、分別計(jì)算該公司在各行時(shí)返回的股權(quán)層級(jí)(F公司就是3和4)
3晦嵌、根據(jù)問題的初始規(guī)定原則同辣,取過程2中的最小值
這里有個(gè)一非常繞的情境,就是F的母公司E惭载,在各行也出現(xiàn)了兩次旱函,兩種回A的路徑分別是ABCDE和ABCE,這樣返回的F股權(quán)層級(jí)就是6和5描滔,按照規(guī)定原則棒妨,結(jié)果應(yīng)該是5(即選擇ABCE路徑),所以在遞歸函數(shù)的定義中含长,必須用循環(huán)將最佳回A路徑找出券腔。
首先定義遞歸函數(shù)
#2020/3/7改進(jìn)
#3/7改進(jìn)2
def levelsearch(level,position,name):#level為初始層級(jí),一般為1拘泞,position為子公司所在行數(shù)
if name=='A':#name記錄當(dāng)前子公司的母公司纷纫,如果為A則跳出遞歸程序
return(level,position,name)
else:
if Ctrsht.iloc[position,1]==name:
if Ctrsht.iloc[position,0]=='A':#如果當(dāng)前子公司的母公司為A,則層級(jí)加1陪腌,遞歸結(jié)束
return(level+1,position,'A')
else:#如果當(dāng)前子公司的母公司不為A辱魁,先記錄母公司在“子公司”列出現(xiàn)的行數(shù),然后調(diào)用程序自身
temPosition=[]
for i in range(len(Ctrsht['子公司'])):
if Ctrsht.iloc[i,1]==Ctrsht.iloc[position,0]:
temPosition.append(i)#母公司在“子公司”列出現(xiàn)的行數(shù)可能為多個(gè)诗鸭,必須用數(shù)組記錄染簇,并遍歷數(shù)組調(diào)用遞歸函數(shù)
templevel={}#用字典記錄母公司每次返回的股權(quán)層級(jí)
levelsig=0
for k in temPosition:
templevel[k]=levelsearch(levelsig,k,Ctrsht.iloc[k,1])[0]
minilevel = min(templevel, key=lambda x:templevel[x])
return(levelsearch(level+1,minilevel,Ctrsht.iloc[minilevel,1]))
else:
pass
然后對(duì)每個(gè)子公司分別計(jì)算出現(xiàn)的行數(shù),并調(diào)用遞歸函數(shù)計(jì)算min{基于各行數(shù)計(jì)算的股權(quán)層級(jí)}
a=Ctrsht['子公司'].unique().tolist()#去除所有的非重復(fù)機(jī)構(gòu)
b=[]#記錄每個(gè)機(jī)構(gòu)的層級(jí)
for i in range(len(a)):
linenum=[]#獲得每個(gè)機(jī)構(gòu)在持股機(jī)構(gòu)列中出現(xiàn)的行數(shù)
for k in range(len(Ctrsht['子公司'])):
if Ctrsht.iloc[k,1]==a[i]:
linenum.append(k)
agencylevel=[]#記錄不同出現(xiàn)行數(shù)計(jì)算的股權(quán)層級(jí)
for j in linenum:
agencylevel.append(levelsearch(1,j,a[i])[0])
b.append(min(agencylevel))
equitylevel=pd.DataFrame(a)
equitylevel['改進(jìn)的股權(quán)層級(jí)']=b
equitylevel
可以看到E的股權(quán)層級(jí)得到了修正强岸。
下面這個(gè)圖可以看到锻弓,F(xiàn)的層級(jí)是在比較3和5之后,選擇的最小值请唱,E的層級(jí)是在比較4和5之后弥咪,選擇的最小值
結(jié)語:遞歸是目前我寫過最難的程序了,對(duì)邏輯的要求高十绑,中間的各種計(jì)數(shù)變量聚至、臨時(shí)變量也多,大腦要清醒本橙,但遞歸代碼的簡潔和高效也使遞歸成為應(yīng)用非常廣泛的算法
算是剛?cè)肓碎T扳躬,以后再修煉吧~