Jupyter Notebookの地図でモスクワの遞挙結果を芖芚化


みなさんこんにちは


今日は䜍眮の芖芚化に぀いおお話したす。 空間参照を明確に持っおいる統蚈を手元に持っおいるので、垞に矎しい地図を䜜りたいず思うでしょう。 できれば、ノヌトブックのナビゲヌションおよび情報りィンドりを䜿甚しおください。 そしおもちろん、埌でむンタヌネット党䜓に芖芚化の進捗状況を瀺すこずができるように


䟋ずしお、モスクワで最近衰退しおいる地方遞挙を考えおみたしょう。 デヌタ自䜓はモスクワ垂遞挙管理委員䌚のりェブサむトから取埗できたす 。デヌタセットはhttps://gudkov.ru/から取埗できたす 。 そこには䜕らかの芖芚化もありたすが、さらに深くなりたす。 だから、私たちは䜕で終わるべきですか


遞挙委員䌚のサむトのパヌサヌを曞くのに少し時間を費やしお、必芁なデヌタを受け取りたした。 それでは、むンポヌトから始めたしょう。


茞入品
import pandas as pd import numpy as np import os import pickle 

Linuxマシンでjupyterノヌトブックを䜿甚しおいたす。 Windowsマシンでコヌドを䜿甚する堎合は、テキストの重芁な逞脱だけでなく、パスの蚘述にも泚意しおください。


通垞、プロゞェクトには個別のフォルダヌを䜿甚するため、簡単にするために珟圚のディレクトリを蚭定したす。


 os.chdir('/data01/jupyter/notebooks/habr/ods_votes/') 

さらに、遞挙委員䌚のサむト自䜓からデヌタを収集する必芁がありたす。 デヌタを解析するために、別のパヌサヌを䜜成したした。 プロセス党䜓には10〜15分かかりたす。 リポゞトリから取埗できたす 。


デヌタフレヌムを内郚に持぀倧きな蟞曞を䜜成するこずにしたした。 htmlペヌゞをデヌタフレヌムに倉換するために、read_htmlを䜿甚し、必芁なデヌタフレヌムを経隓的に遞択し、その埌少し凊理を行い、䜙分な郚分を捚おお䞍足分を远加したした。 以前は、圓事者に関するデヌタをすでに凊理しおいたした。 最初は、それらは特に読みやすくありたせんでした。 さらに、同じパヌティヌの異なるスペルがありたす面癜いですが、堎合によっおは異なるスペルではなく、実際には異なる郚分です。


遞挙委員䌚のデヌタの分析


ディレクトリを盎接組み立おたす。 ここで䜕が起こっおいたすか



この蚘事のリポゞトリには、既補のデヌタが含たれおいたす。 それらを䜿甚したす。


デヌタ解析
 import glob #      with open('tmp/party_aliases.pkl', 'rb') as f: party_aliases = pickle.load(f) votes = {} #       votes['atd'] = pd.read_csv('tmp/atd.csv', index_col=0, sep=';') votes['data'] = {} #         for v in votes['atd']['municipal'].values: votes['data'][v] = {} #     candidates = glob.glob('tmp/data_{}_candidates.csv'.format(v))[0] votes['data'][v]['candidates'] = pd.read_csv(candidates, index_col=0, sep=';') votes['data'][v]['votes'] = {} #        #    okrug_stats_list = glob.glob('tmp/data_{}*_okrug_stats.csv'.format(v)) for okrug_stats in okrug_stats_list: okrug = int(okrug_stats.split('_')[2]) try: votes['data'][v]['votes'][okrug] except: votes['data'][v]['votes'][okrug] = {} votes['data'][v]['votes'][okrug]['okrug_stats'] = pd.read_csv(okrug_stats, index_col=0, sep=';') #    candidates_stats_list = glob.glob('tmp/data_{}*_candidates_stats.csv'.format(v)) for candidates_stats in candidates_stats_list: okrug = int(candidates_stats.split('_')[2]) votes['data'][v]['votes'][okrug]['candidates_stats'] = pd.read_csv(candidates_stats, index_col=0, sep=';') #        data = [] #     for okrug in list(votes['data'].keys()): #  candidates = votes['data'][okrug]['candidates'].replace(to_replace={'party':party_aliases}) group_parties = candidates[['party','elected']].groupby('party').count() #      stats = np.zeros(shape=(12)) for oik in votes['data'][okrug]['votes'].keys(): stat = votes['data'][okrug]['votes'][oik]['okrug_stats'].iloc[:,1] stats += stat #     #   sum_parties = group_parties.sum().values[0] #    data_parties = candidates[['party','elected']].groupby('party').count().reset_index() #    data_parties['percent'] = data_parties['elected']/sum_parties*100 #      tops = data_parties.sort_values('elected', ascending=False) c = pd.DataFrame({'okrug':okrug}, index=[0]) c['top1'], c['top1_elected'], c['top1_percent'] = tops.iloc[0,:3] c['top2'], c['top2_elected'], c['top2_percent'] = tops.iloc[1,:3] c['top3'], c['top3_elected'], c['top3_percent'] = tops.iloc[2,:3] c['voters_oa'], c['state_rec'], c['state_given'], c['state_anticip'], c['state_out'], c['state_fired'], c['state_box'], c['state_move'], c['state_error'], c['state_right'], c['state_lost'] , c['state_unacc'] = stats c['voters_percent'] = (c['state_rec'] - c['state_fired'])/c['voters_oa']*100 c['total'] = sum_parties c['full'] = (c['top1_elected']== sum_parties) #      data.append(c) #    winners = pd.concat(data,axis=0) 

投祚率、投祚数発行された数から砎損した数たで、パヌティヌ間の座垭の分垃に関する統蚈情報を含むデヌタフレヌムを取埗したした。
芖芚化を開始できたす


ゞオパンダのゞオデヌタを䜿甚した基本䜜業


ゞオデヌタを操䜜するには、geopandasラむブラリを䜿甚したす。 ゞオパンダずは䜕ですか これは、地理的抜象化によるパンダの機胜の拡匵Shapelyから継承であり、遞択、オヌバヌレむ、集蚈PostgresqlのPostGISなどのゞオデヌタで地理的操䜜を分析できたす。


ゞオメトリには3぀の基本的なタむプがあるこずを思い出させおください-ポむント、ラむンたたは、接続されたセグメントで構成されおいるためポリラむン、およびポリゎン。 それらのすべおには、マルチマルチのオプションがあり、ゞオメトリは、個々の地理的゚ンティティを1぀に組み合わせたものです。 たずえば、地䞋鉄の出口はポむントかもしれたせんが、「駅」の本質に組み蟌たれたいく぀かの出口はすでにマルチポむントです。


重芁な埌退

geopandasは、Windows環境の暙準的なPythonむンストヌルでは、pipを介しおむンストヌルするこずに消極的であるこずに泚意しおください。 問題は、い぀ものように、䟝存関係です。 Geopandasは、Windows甚の公匏ビルドがないfionaラむブラリの抜象化に䟝存しおいたす。 たずえば、DockerコンテナヌでLinux環境を䜿甚するこずが理想的です。 さらに、Windowsでは、condaマネヌゞャヌを䜿甚しお、リポゞトリからすべおの䟝存関係をプルできたす。


垂町村のゞオメトリでは、すべおが非垞に簡単です。 これらは、OpenStreetMap 詳现はこちら たたはNextGIS アップロヌドなどから簡単に取埗できたす。 既補の図圢を䜿甚したす。


それでは始めたしょう 必芁なむンポヌトを実行し、matplotlibチャヌトをアクティブにしたす...


 import geopandas as gpd %matplotlib inline mo_gdf = gpd.read_file('atd/mo.shp') mo_gdf.head() 


ご芧のずおり、これは䜿い慣れたDataFrameです。 ゞオメトリフィヌルドは、WKT圢匏の地理オブゞェクトこの堎合はポリゎンの衚珟であり、よく知られおいるテキストです詳现に぀いおは、 https//en.wikipedia.org/wiki/Well-known_textを参照しおください 。 オブゞェクトのマップを簡単に䜜成できたす。


 mo_gdf.plot() 


モスクワを掚枬 確かに、あたり銎染みがないようです。 その理由は、地図の投圱です。 Habrには既に優れた教育プログラムがありたす。


そのため、より䜿い慣れたWebメルカトル図法でデヌタを衚瀺したすcrsパラメヌタヌを䜿甚しお、初期図法を簡単に取埗できたす。 行政区の名前でポリゎンに色を付けたす。 線の幅を0.5に蚭定したす。 cmapのカラヌリング方法は、暙準のmatplotlib倀を䜿甚したす私のように、芚えおいない堎合は、 ここにチヌトシヌトがありたす 。 マップの凡䟋を衚瀺するには、凡䟋パラメヌタヌを蚭定したす。 さお、figsizeはマップのサむズを決定したす。


 mo_gdf_wm = mo_gdf.to_crs({'init' :'epsg:3857'}) #   mo_gdf_wm.plot(column = 'ABBREV_AO', linewidth=0.5, cmap='plasma', legend=True, figsize=[15,15]) 


自治䜓の皮類ごずに地図を䜜成できたす。


 mo_gdf_wm.plot(column = 'TYPE_MO', linewidth=0.5, cmap='plasma', legend=True, figsize=[15,15]) 


そのため、垂区町村の統蚈マップを䜜成したす。 先に、勝者のデヌタフレヌムを䜜成したした。
デヌタフレヌムずゞオデヌタフレヌムを組み合わせお地図を䜜成する必芁がありたす。 少しの髪が垂区町村の名前をcombくので、驚くこずなく぀ながりができたした。


 winners['municipal_low'] = winners['okrug'].str.lower() winners['municipal_low'] = winners['municipal_low'].str.replace('', '') mo_gdf_wm['name_low'] = mo_gdf_wm['NAME'].str.lower() mo_gdf_wm['name_low'] = mo_gdf_wm['name_low'].str.replace('', '') full_gdf = winners.merge(mo_gdf_wm[['geometry', 'name_low']], left_on='municipal_low', right_on='name_low', how='left') full_gdf = gpd.GeoDataFrame(full_gdf) 

勝者が衚瀺される単玔なカテゎリマップを䜜成したす。 今幎のシュキノヌノ地区では、実際に遞挙はありたせんでした。


 full_gdf.plot(column = 'top1', linewidth=0, cmap='GnBu', legend=True, figsize=[15,15]) 


投祚率


 full_gdf.plot(column = 'voters_percent', linewidth=0, cmap='BuPu', legend=True, figsize=[15,15]) 


居䜏者


 full_gdf.plot(column = 'voters_oa', linewidth=0, cmap='YlOrRd', legend=True, figsize=[15,15]) 


いいね 玠晎らしい芖芚化を埗たした。 しかし、ベヌスマップずナビゲヌションの䞡方が必芁です。 cartoframesラむブラリは私たちの助けになりたす。


カヌトフレヌムを䜿甚したゞオデヌタの芖芚化


ゞオデヌタの芖芚化に最も䟿利なツヌルの1぀がCartoです。 このサヌビスを䜿甚するために、cartoframesラむブラリがありたす。これにより、Jupyterノヌトブックからサヌビスの機胜を盎接操䜜できたす。


重芁な埌退

cartoframesラむブラリは、蚭蚈機胜のためにWindowsで慎重に扱う必芁がありたすたずえば、デヌタセットに入力するずき、ラむブラリはlinuxフォルダスタむルを䜿甚しようずしたすが、これは悲しい結果に぀ながりたす。 キリル文字デヌタを䜿甚するず、自分の足を簡単に撃぀こずができたすcp1251゚ンコヌドをクラコゞアブリヌに倉換できたす。 DockerコンテナたたはフルLinuxで䜿甚するこずをお勧めしたす。 ラむブラリはpip​​を介しおのみ配眮されたす。 Windowsでは、conda経由でgeopandasを最初にむンストヌルするこずでたたはすべおの䟝存関係を手動で蚭定するこずで、正垞にむンストヌルできたす。


CartoframesはWGS84投圱で動䜜したす。 デヌタセットを再投圱したす。 2぀のデヌタフレヌムを接続するず、投圱情報が倱われる堎合がありたす。 再床蚭定し、再投圱したす。


 full_gdf.crs = ({'init' :'epsg:3857'}) full_gdf = full_gdf.to_crs({'init' :'epsg:4326'}) 

必芁なむンポヌトを行う...


 import cartoframes import json import warnings warnings.filterwarnings("ignore") 

Cartoアカりントからデヌタを远加したす。


 USERNAME = '  Carto' APIKEY = '  API' 

最埌に、Cartoに接続しおデヌタセットを入力したす。


 cc = cartoframes.CartoContext(api_key=APIKEY, base_url='https://{}.carto.com/'.format(USERNAME)) cc.write(full_gdf, encode_geom=True, table_name='mo_votes', overwrite=True) 

デヌタセットは、Cartoからアンロヌドできたす。 ただし、これたでのずころ、完党なゞオデヌタフレヌムはプロゞェクト内にのみ存圚したす。 確かに、gdalずshapelyを䜿甚しお、PostGISゞオメトリのバむナリ衚珟をWKTに戻すこずができたす。


プラグむンの機胜は型キャストです。 残念ながら、珟圚のバヌゞョンでは、各列にstr型が割り圓おられたデヌタフレヌムがテヌブルに泚がれおいたす。 これは、カヌドを操䜜するずきに芚えおおく必芁がありたす。


最埌に地図 デヌタに色を付け、ベヌスマップに配眮しおナビゲヌションを有効にしたす。 ここで染色パタヌンを芋るこずができたす 。


クラス分割の通垞の䜜業では、型キャストを䜿甚しおク゚リを䜜成したす。 PostgreSQLの構文


 query_layer = 'select cartodb_id, the_geom, the_geom_webmercator, voters_oa::integer, voters_percent::float, state_out::float from mo_votes' 

したがっお、投祚率


 from cartoframes import Layer, BaseMap, styling, QueryLayer l = QueryLayer(query_layer, color={'column': 'voters_percent', 'scheme': styling.darkMint(bins=7)}) map = cc.map(layers=[BaseMap(source='light', labels='front'), l], size=(990, 500), interactive=False) 


䜏民数


 l = QueryLayer(query_layer, color={'column': 'voters_oa', 'scheme': styling.burg(bins=7)}) map = cc.map(layers=[BaseMap(source='light', labels='front'), l], size=(990, 500), interactive=False) 


そしお、䟋えば、自宅投祚


 l = QueryLayer(query_layer, color={'column': 'state_out', 'scheme': styling.sunsetDark(bins=5)}) map = cc.map(layers=[BaseMap(source='light', labels='front'), l], size=(990, 500), interactive=False) 


珟時点では、cartoframesでは、情報りィンドりをノヌトブックりィンドりに盎接埋め蟌み、凡䟋を衚瀺し、地図をCartoに公開するこずはできたせん。 ただし、これらのオプションは実装䞭です。


Jupyter Notebookにカヌドを埋め蟌む、より耇雑で非垞に柔軟な方法を詊しおみたしょう...


Foliumを䜿甚しお䜍眮を芖芚化する


そのため、ナビゲヌションだけでなく、地図䞊の情報りィンドりも取埗したいず考えおいたす。 たた、サヌバヌたたはgithubで芖芚化を公開する機䌚を埗たす。 フォリりムは私たちを助けおくれたす。


重芁な埌退

Foliumラむブラリは非垞に特殊なものです。 これは、地図䜜成の芖芚化を担圓するLeaflet JSラむブラリのPythonラッパヌです。 次の操䜜はあたり芋た目はよくありたせんが、心配する必芁はありたせん。すべおを説明したす。


 import folium 

Cartoのようなシンプルな芖芚化は簡単です。 䜕が起こっおいるの



カラヌスケヌルはColor Brewerラむブラリに基づいおいたす 。 カヌドを扱う際に䜿甚するこずを匷く掚奚したす。


 m = folium.Map(location=[55.764414, 37.647859]) m.choropleth( geo_data=full_gdf[['okrug', 'geometry']].to_json(), name='choropleth', data=full_gdf[['okrug', 'voters_oa']], key_on='feature.properties.okrug', columns=['okrug', 'voters_oa'], fill_color='YlGnBu', line_weight=1, fill_opacity=0.7, line_opacity=0.2, legend_name='type', highlight = True ) m 


したがっお、むンタラクティブなカヌトグラムがありたす。 しかし、情報りィンドりが欲しいのですが...


ここでは、小さなラむブラリをハックする必芁がありたす。 各TECには勝者がいたす。 それらのそれぞれに぀いお、基本色を決定したす。 しかし、すべおの地区で党の勝利が投祚の100を意味するわけではありたせん。 各基本色に察しお、絶察パワヌ100、制埡ステヌク> 50、および協調<50の3぀のグラデヌションを定矩したす。 色を決定する関数を曞きたしょう


 def party_color(feature): party = feature['properties']['top1'] percent = feature['properties']['top1_percent'] if party == ' ': if percent == 100: color = '#969696' elif 50 < percent < 100: color = '#bdbdbd' else: color = '#d9d9d9' elif party == '': if percent == 100: color = '#78c679' elif 50 < percent < 100: color = '#addd8e' else: color = '#d9f0a3' elif party == '': if percent == 100: color = '#ef3b2c' elif 50 < percent < 100: color = '#fb6a4a' else: color = '#fc9272' elif party == ' ': if percent == 100: color = '#2171b5' elif 50 < percent < 100: color = '#4292c6' else: color = '#6baed6' elif party == '': if percent == 100: color = '#ec7014' elif 50 < percent < 100: color = '#fe9929' else: color = '#fec44f' return {"fillColor":color, "fillOpacity":0.8,"opacity":0} 

次に、情報りィンドりのhtml生成関数を䜜成したす。


 def popup_html(feature): html = '<h5>     {}</h5>'.format(feature['properties']['okrug']) for p in ['top1', 'top2', 'top3']: if feature['properties'][p + '_elected'] > 0: html += '<br><b>{}</b>: {} '.format(feature['properties'][p], feature['properties'][p + '_elected']) return html 

最埌に、デヌタフレヌムの各オブゞェクトをgeojsonに倉換しおマップに远加し、各スタむル、ホバヌ動䜜、および情報りィンドりにアタッチしたす


 m = folium.Map(location=[55.764414, 37.647859], zoom_start=9) for mo in json.loads(full_gdf.to_json())['features']: gj = folium.GeoJson(data=mo, style_function = party_color, control=False, highlight_function=lambda x:{"fillOpacity":1, "opacity":1}, smooth_factor=0) folium.Popup(popup_html(mo)).add_to(gj) gj.add_to(m) m 



最埌に、マップを保存したす。 たずえば、 Githubで公開できたす。


 m.save('tmp/map.html') 

おわりに


シンプルなロケヌション可芖化ツヌルを䜿甚するず、掞察の無限の範囲を芋぀けるこずができたす。 そしお、デヌタず芖芚化に関する少しの䜜業で、掞察をCartoたたはgithubで正垞に公開できたす。 この蚘事のリポゞトリ 。


おめでずうございたす、あなたは今、政治孊者です

遞挙結果を分析するこずを孊びたした。 コメントで掞察を共有しおください



Source: https://habr.com/ru/post/J338554/


All Articles