一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

7 款 Python 數(shù)據(jù)圖表工具的比較

 imelee 2017-02-09



Python 的 科學(xué)棧 相當(dāng)成熟,各種應(yīng)用場(chǎng)景都有相關(guān)的模塊,包括 機(jī)器學(xué)習(xí)數(shù)據(jù)分析 。數(shù)據(jù)可視化是 發(fā)現(xiàn) 數(shù)據(jù)和展示結(jié)果的重要一環(huán),只不過(guò)過(guò)去以來(lái),相對(duì)于 R 這樣的工具,發(fā)展還是落后一些。

幸運(yùn)的是,過(guò)去幾年出現(xiàn)了很多新的Python數(shù)據(jù)可視化庫(kù),彌補(bǔ)了一些這方面的差距。 matplotlib 已經(jīng)成為事實(shí)上的數(shù)據(jù)可視化方面最主要的庫(kù),此外還有很多其他庫(kù),例如 vispy, bokeh , seaborn ,  pyga, foliumnetworkx ,這些庫(kù)有些是構(gòu)建在 matplotlib 之上,還有些有其他一些功能。

本文會(huì)基于一份真實(shí)的數(shù)據(jù),使用這些庫(kù)來(lái)對(duì)數(shù)據(jù)進(jìn)行可視化。通過(guò)這些對(duì)比,我們期望了解每個(gè)庫(kù)所適用的范圍,以及如何更好的利用整個(gè) Python 的數(shù)據(jù)可視化的生態(tài)系統(tǒng)。

我們?cè)?Dataquest 建了一個(gè)交互課程,教你如何使用 Python 的數(shù)據(jù)可視化工具。如果你打算深入學(xué)習(xí),可以點(diǎn) 這里 。

探索數(shù)據(jù)集

在我們探討數(shù)據(jù)的可視化之前,讓我們先來(lái)快速的瀏覽一下我們將要處理的數(shù)據(jù)集。我們將要使用的數(shù)據(jù)來(lái)自 openflights 。我們將要使用 航線(xiàn)數(shù)據(jù)集 、 機(jī)場(chǎng)數(shù)據(jù)集 、 航空公司數(shù)據(jù)集 。其中,路徑數(shù)據(jù)的每一行對(duì)應(yīng)的是兩個(gè)機(jī)場(chǎng)之間的飛行路徑;機(jī)場(chǎng)數(shù)據(jù)的每一行對(duì)應(yīng)的是世界上的某一個(gè)機(jī)場(chǎng),并且給出了相關(guān)信息;航空公司的數(shù)據(jù)的每一行給出的是每一個(gè)航空公司。

首先我們先讀取數(shù)據(jù):

# Import the pandas library.
import pandas
# Read in the airports data.
airports = pandas.read_csv("airports.csv", header=None, dtype=str)
airports.columns = ["id", "name", "city", "country", "code", "icao", "latitude", "longitude", "altitude", "offset", "dst", "timezone"]
# Read in the airlines data.
airlines = pandas.read_csv("airlines.csv", header=None, dtype=str)
airlines.columns = ["id", "name", "alias", "iata", "icao", "callsign", "country", "active"]
# Read in the routes data.
routes = pandas.read_csv("routes.csv", header=None, dtype=str)
routes.columns = ["airline", "airline_id", "source", "source_id", "dest", "dest_id", "codeshare", "stops", "equipment"]
# Import the pandas library.
import pandas
# Read in the airports data.
airports = pandas.read_csv("airports.csv", header=None, dtype=str)
airports.columns = ["id", "name", "city", "country", "code", "icao", "latitude", "longitude", "altitude", "offset", "dst", "timezone"]
# Read in the airlines data.
airlines = pandas.read_csv("airlines.csv", header=None, dtype=str)
airlines.columns = ["id", "name", "alias", "iata", "icao", "callsign", "country", "active"]
# Read in the routes data.
routes = pandas.read_csv("routes.csv", header=None, dtype=str)
routes.columns = ["airline", "airline_id", "source", "source_id", "dest", "dest_id", "codeshare", "stops", "equipment"]

這些數(shù)據(jù)沒(méi)有列的首選項(xiàng),因此我們通過(guò)賦值 column 屬性來(lái)添加列的首選項(xiàng)。我們想要將每一列作為字符串進(jìn)行讀取,因?yàn)檫@樣做可以簡(jiǎn)化后續(xù)以行 id 為匹配,對(duì)不同的數(shù)據(jù)框架進(jìn)行比較的步驟。我們?cè)谧x取數(shù)據(jù)時(shí)設(shè)置了 dtype 屬性值達(dá)到這一目的。

我們可以快速瀏覽一下每一個(gè)數(shù)據(jù)集的數(shù)據(jù)框架。

airports.head()
airports.head()

Python

airlines.head()
airlines.head()

Python

routes.head()
routes.head()

我們可以分別對(duì)每一個(gè)單獨(dú)的數(shù)據(jù)集做許多不同有趣的探索,但是只要將它們結(jié)合起來(lái)分析才能取得最大的收獲。Pandas 將會(huì)幫助我們分析數(shù)據(jù),因?yàn)樗軌蛴行У倪^(guò)濾權(quán)值或者通過(guò)它來(lái)應(yīng)用一些函數(shù)。我們將會(huì)深入幾個(gè)有趣的權(quán)值因子,比如分析航空公司和航線(xiàn)。

那么在此之前我們需要做一些數(shù)據(jù)清洗的工作。

routes = routes[routes["airline_id"] != "\\N"]
routes = routes[routes["airline_id"] != "\\N"]

這一行命令就確保了我們?cè)?airline_id 這一列只含有數(shù)值型數(shù)據(jù)。

制作柱狀圖

現(xiàn)在我們理解了數(shù)據(jù)的結(jié)構(gòu),我們可以進(jìn)一步地開(kāi)始描點(diǎn)來(lái)繼續(xù)探索這個(gè)問(wèn)題。首先,我們將要使用 matplotlib 這個(gè)工具,matplotlib 是一個(gè)相對(duì)底層的 Python 棧中的描點(diǎn)庫(kù),所以它比其他的工具庫(kù)要多敲一些命令來(lái)做出一個(gè)好看的曲線(xiàn)。另外一方面,你可以使用 matplotlib 幾乎做出任何的曲線(xiàn),這是因?yàn)樗值撵`活,而靈活的代價(jià)就是非常難于使用。

我們首先通過(guò)做出一個(gè)柱狀圖來(lái)顯示不同的航空公司的航線(xiàn)長(zhǎng)度分布。一個(gè)柱狀圖將所有的航線(xiàn)的長(zhǎng)度分割到不同的值域,然后對(duì)落入到不同的值域范圍內(nèi)的航線(xiàn)進(jìn)行計(jì)數(shù)。從中我們可以知道哪些航空公司的航線(xiàn)長(zhǎng),哪些航空公司的航線(xiàn)短。

為了達(dá)到這一點(diǎn),我們需要首先計(jì)算一下航線(xiàn)的長(zhǎng)度,第一步就要使用距離公式,我們將會(huì)使用余弦半正矢距離公式來(lái)計(jì)算經(jīng)緯度刻畫(huà)的兩個(gè)點(diǎn)之間的距離。

import math
def haversine(lon1, lat1, lon2, lat2):
    # Convert coordinates to floats.
    lon1, lat1, lon2, lat2 = [float(lon1), float(lat1), float(lon2), float(lat2)]
    # Convert to radians from degrees.
    lon1, lat1, lon2, lat2 = map(math.radians, [lon1, lat1, lon2, lat2])
    # Compute distance.
    dlon = lon2 - lon1 
    dlat = lat2 - lat1 
    a = math.sin(dlat/2)**2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon/2)**2
    c = 2 * math.asin(math.sqrt(a)) 
    km = 6367 * c
    return km
import math
def haversine(lon1, lat1, lon2, lat2):
    # Convert coordinates to floats.
    lon1, lat1, lon2, lat2 = [float(lon1), float(lat1), float(lon2), float(lat2)]
    # Convert to radians from degrees.
    lon1, lat1, lon2, lat2 = map(math.radians, [lon1, lat1, lon2, lat2])
    # Compute distance.
    dlon = lon2 - lon1 
    dlat = lat2 - lat1 
    a = math.sin(dlat/2)**2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon/2)**2
    c = 2 * math.asin(math.sqrt(a)) 
    km = 6367 * c
    return km

然后我們就可以使用一個(gè)函數(shù)來(lái)計(jì)算起點(diǎn)機(jī)場(chǎng)和終點(diǎn)機(jī)場(chǎng)之間的單程距離。我們需要從路線(xiàn)數(shù)據(jù)框架得到機(jī)場(chǎng)數(shù)據(jù)框架所對(duì)應(yīng)的 source_id 和 dest_id,然后與機(jī)場(chǎng)的數(shù)據(jù)集的 id 列相匹配,然后就只要計(jì)算就行了,這個(gè)函數(shù)是這樣的:

def calc_dist(row):
    dist = 0
    try:
        # Match source and destination to get coordinates.
        source = airports[airports["id"] == row["source_id"]].iloc[0]
        dest = airports[airports["id"] == row["dest_id"]].iloc[0]
        # Use coordinates to compute distance.
        dist = haversine(dest["longitude"], dest["latitude"], source["longitude"], source["latitude"])
    except (ValueError, IndexError):
        pass
    return dist
def calc_dist(row):
    dist = 0
    try:
        # Match source and destination to get coordinates.
        source = airports[airports["id"] == row["source_id"]].iloc[0]
        dest = airports[airports["id"] == row["dest_id"]].iloc[0]
        # Use coordinates to compute distance.
        dist = haversine(dest["longitude"], dest["latitude"], source["longitude"], source["latitude"])
    except (ValueError, IndexError):
        pass
    return dist

如果 source_id 和 dest_id 列沒(méi)有有效值的話(huà),那么這個(gè)函數(shù)會(huì)報(bào)錯(cuò)。因此我們需要增加 try/catch 模塊對(duì)這種無(wú)效的情況進(jìn)行捕捉。

最后,我們將要使用 pandas 來(lái)將距離計(jì)算的函數(shù)運(yùn)用到 routes 數(shù)據(jù)框架。這將會(huì)使我們得到包含所有的航線(xiàn)線(xiàn)長(zhǎng)度的 pandas 序列,其中航線(xiàn)線(xiàn)的長(zhǎng)度都是以公里做單位。

route_lengths = routes.apply(calc_dist, axis=1)
route_lengths = routes.apply(calc_dist, axis=1)

現(xiàn)在我們就有了航線(xiàn)距離的序列了,我們將會(huì)創(chuàng)建一個(gè)柱狀圖,它將會(huì)將數(shù)據(jù)歸類(lèi)到對(duì)應(yīng)的范圍之內(nèi),然后計(jì)數(shù)分別有多少的航線(xiàn)落入到不同的每個(gè)范圍:

import matplotlib.pyplot as plt 
%matplotlib inline 

plt.hist(route_lengths, bins=20)
import matplotlib.pyplot as plt 
%matplotlib inline 
 
 
plt.hist(route_lengths, bins=20)

我們用 import matplotlib.pyplot as plt 導(dǎo)入 matplotlib 描點(diǎn)函數(shù)。然后我們就使用 %matplotlib inline 來(lái)設(shè)置 matplotlib 在 ipython 的 notebook 中描點(diǎn),最終我們就利用 plt.hist(route_lengths, bins=20) 得到了一個(gè)柱狀圖。正如我們看到的,航空公司傾向于運(yùn)行近距離的短程航線(xiàn),而不是遠(yuǎn)距離的遠(yuǎn)程航線(xiàn)。

使用 seaborn

我們可以利用 seaborn 來(lái)做類(lèi)似的描點(diǎn),seaborn 是一個(gè) Python 的高級(jí)庫(kù)。Seaborn 建立在 matplotlib 的基礎(chǔ)之上,做一些類(lèi)型的描點(diǎn),這些工作常常與簡(jiǎn)單的統(tǒng)計(jì)工作有關(guān)。我們可以基于一個(gè)核心的概率密度的期望,使用 distplot 函數(shù)來(lái)描繪一個(gè)柱狀圖。一個(gè)核心的密度期望是一個(gè)曲線(xiàn) —— 本質(zhì)上是一個(gè)比柱狀圖平滑一點(diǎn)的,更容易看出其中的規(guī)律的曲線(xiàn)。

import seaborn 
seaborn.distplot(route_lengths, bins=20)
import seaborn 
seaborn.distplot(route_lengths, bins=20)

正如你所看到的那樣,seaborn 同時(shí)有著更加好看的默認(rèn)風(fēng)格。seaborn 不含有與每個(gè) matplotlib 的版本相對(duì)應(yīng)的版本,但是它的確是一個(gè)很好的快速描點(diǎn)工具,而且相比于 matplotlib 的默認(rèn)圖表可以更好的幫助我們理解數(shù)據(jù)背后的含義。如果你想更深入的做一些統(tǒng)計(jì)方面的工作的話(huà),seaborn 也不失為一個(gè)很好的庫(kù)。

條形圖

柱狀圖也雖然很好,但是有時(shí)候我們會(huì)需要航空公司的平均路線(xiàn)長(zhǎng)度。這時(shí)候我們可以使用條形圖--每條航線(xiàn)都會(huì)有一個(gè)單獨(dú)的狀態(tài)條,顯示航空公司航線(xiàn)的平均長(zhǎng)度。從中我們可以看出哪家是國(guó)內(nèi)航空公司哪家是國(guó)際航空公司。我們可以使用pandas,一個(gè)python的數(shù)據(jù)分析庫(kù),來(lái)酸楚每個(gè)航空公司的平均航線(xiàn)長(zhǎng)度。

import numpy
# Put relevant columns into a dataframe.
route_length_df = pandas.DataFrame({"length": route_lengths, "id": routes["airline_id"]})
# Compute the mean route length per airline.
airline_route_lengths = route_length_df.groupby("id").aggregate(numpy.mean)
# Sort by length so we can make a better chart.
airline_route_lengths = airline_route_lengths.sort("length", ascending=False)
import numpy
# Put relevant columns into a dataframe.
route_length_df = pandas.DataFrame({"length": route_lengths, "id": routes["airline_id"]})
# Compute the mean route length per airline.
airline_route_lengths = route_length_df.groupby("id").aggregate(numpy.mean)
# Sort by length so we can make a better chart.
airline_route_lengths = airline_route_lengths.sort("length", ascending=False)

我們首先用航線(xiàn)長(zhǎng)度和航空公司的id來(lái)搭建一個(gè)新的數(shù)據(jù)框架。我們基于airline_id把route_length_df拆分成組,為每個(gè)航空公司建立一個(gè)大體的數(shù)據(jù)框架。然后我們調(diào)用pandas的aggregate函數(shù)來(lái)獲取航空公司數(shù)據(jù)框架中長(zhǎng)度列的均值,然后把每個(gè)獲取到的值重組到一個(gè)新的數(shù)據(jù)模型里。之后把數(shù)據(jù)模型進(jìn)行排序,這樣就使得擁有最多航線(xiàn)的航空公司拍到了前面。

這樣就可以使用matplotlib把結(jié)果畫(huà)出來(lái)。

plt.bar(range(airline_route_lengths.shape[0]), airline_route_lengths["length"])
plt.bar(range(airline_route_lengths.shape[0]), airline_route_lengths["length"])

Matplotlib的plt.bar方法根據(jù)每個(gè)數(shù)據(jù)模型的航空公司平均航線(xiàn)長(zhǎng)度(airline_route_lengths["length"])來(lái)做圖。

問(wèn)題是我們想看出哪家航空公司擁有的航線(xiàn)長(zhǎng)度是什么并不容易。為了解決這個(gè)問(wèn)題,我們需要能夠看到坐標(biāo)軸標(biāo)簽。這有點(diǎn)難,畢竟有這么多的航空公司。一個(gè)能使問(wèn)題變得簡(jiǎn)單的方法是使圖表具有交互性,這樣能實(shí)現(xiàn)放大跟縮小來(lái)查看軸標(biāo)簽。我們可以使用bokeh庫(kù)來(lái)實(shí)現(xiàn)這個(gè)--它能便捷的實(shí)現(xiàn)交互性,作出可縮放的圖表。

要使用booked,我們需要先對(duì)數(shù)據(jù)進(jìn)行預(yù)處理:

def lookup_name(row):
    try:
        # Match the row id to the id in the airlines dataframe so we can get the name.
        name = airlines["name"][airlines["id"] == row["id"]].iloc[0]
    except (ValueError, IndexError):
        name = ""
    return name
# Add the index (the airline ids) as a column.
airline_route_lengths["id"] = airline_route_lengths.index.copy()
# Find all the airline names.
airline_route_lengths["name"] = airline_route_lengths.apply(lookup_name, axis=1)
# Remove duplicate values in the index.
airline_route_lengths.index = range(airline_route_lengths.shape[0])
def lookup_name(row):
    try:
        # Match the row id to the id in the airlines dataframe so we can get the name.
        name = airlines["name"][airlines["id"] == row["id"]].iloc[0]
    except (ValueError, IndexError):
        name = ""
    return name
# Add the index (the airline ids) as a column.
airline_route_lengths["id"] = airline_route_lengths.index.copy()
# Find all the airline names.
airline_route_lengths["name"] = airline_route_lengths.apply(lookup_name, axis=1)
# Remove duplicate values in the index.
airline_route_lengths.index = range(airline_route_lengths.shape[0])

上面的代碼會(huì)獲取airline_route_lengths中每列的名字,然后添加到name列上,這里存貯著每個(gè)航空公司的名字。我們也添加到id列上以實(shí)現(xiàn)查找(apply函數(shù)不傳index)。

最后,我們重置索引序列以得到所有的特殊值。沒(méi)有這一步,Bokeh 無(wú)法正常運(yùn)行。

現(xiàn)在,我們可以繼續(xù)說(shuō)圖表問(wèn)題:

import numpy as np
from bokeh.io import output_notebook
from bokeh.charts import Bar, show
output_notebook()
p = Bar(airline_route_lengths, 'name', values='length', title="Average airline route lengths")
show(p)
import numpy as np
from bokeh.io import output_notebook
from bokeh.charts import Bar, show
output_notebook()
p = Bar(airline_route_lengths, 'name', values='length', title="Average airline route lengths")
show(p)

用 output_notebook 創(chuàng)建背景虛化,在 iPython 的 notebook 里畫(huà)出圖。然后,使用數(shù)據(jù)幀和特定序列制作條形圖。最后,顯示功能會(huì)顯示出該圖。

這個(gè)圖實(shí)際上不是一個(gè)圖像--它是一個(gè) JavaScript 插件。因此,我們?cè)谙旅嬲故镜氖且环聊唤貓D,而不是真實(shí)的表格。

有了它,我們可以放大,看哪一趟航班的飛行路線(xiàn)最長(zhǎng)。上面的圖像讓這些表格看起來(lái)擠在了一起,但放大以后,看起來(lái)就方便多了。

水平條形圖

Pygal 是一個(gè)能快速制作出有吸引力表格的數(shù)據(jù)分析庫(kù)。我們可以用它來(lái)按長(zhǎng)度分解路由。首先把我們的路由分成短、中、長(zhǎng)三個(gè)距離,并在 route_lengths 里計(jì)算出它們各占的百分比。

Python

long_routes = len([k for k in route_lengths if k > 10000]) / len(route_lengths)
medium_routes = len([k for k in route_lengths if k < 10000 and k > 2000]) / len(route_lengths)
short_routes = len([k for k in route_lengths if k < 2000]) / len(route_lengths)
long_routes = len([k for k in route_lengthsif k > 10000]) / len(route_lengths)
medium_routes = len([k for k in route_lengthsif k < 10000 and k > 2000]) / len(route_lengths)
short_routes = len([k for k in route_lengthsif k < 2000]) / len(route_lengths)

然后我們可以在 Pygal 的水平條形圖里把每一個(gè)都繪成條形圖:

首先,我們創(chuàng)建一個(gè)空?qǐng)D。然后,我們添加元素,包括標(biāo)題和條形圖。每個(gè)條形圖通過(guò)百分比值(最大值是100)顯示出該類(lèi)路由的使用頻率。

最后,我們把圖表渲染成文件,用 IPython 的 SVG 功能載入并展示文件。這個(gè)圖看上去比默認(rèn)的 matplotlib 圖好多了。但是為了制作出這個(gè)圖,我們要寫(xiě)的代碼也多很多。因此,Pygal 可能比較適用于制作小型的展示用圖表。

散點(diǎn)圖

在散點(diǎn)圖里,我們能夠縱向比較數(shù)據(jù)。我們可以做一個(gè)簡(jiǎn)單的散點(diǎn)圖來(lái)比較航空公司的 id 號(hào)和航空公司名稱(chēng)的長(zhǎng)度:

Python

name_lengths = airlines["name"].apply(lambda x: len(str(x)))
plt.scatter(airlines["id"].astype(int), name_lengths)
name_lengths = airlines["name"].apply(lambda x: len(str(x)))
plt.scatter(airlines["id"].astype(int), name_lengths)

首先,我們使用 pandasapplymethod 計(jì)算每個(gè)名稱(chēng)的長(zhǎng)度。它將找到每個(gè)航空公司的名字字符的數(shù)量。然后,我們使用 matplotlib 做一個(gè)散點(diǎn)圖來(lái)比較航空 id 的長(zhǎng)度。當(dāng)我們繪制時(shí),我們把 theidcolumn of airlines 轉(zhuǎn)換為整數(shù)類(lèi)型。如果我們不這樣做是行不通的,因?yàn)樗枰?x 軸上的數(shù)值。我們可以看到不少的長(zhǎng)名字都出現(xiàn)在早先的 id 中。這可能意味著航空公司在成立前往往有較長(zhǎng)的名字。

我們可以使用 seaborn 驗(yàn)證這個(gè)直覺(jué)。Seaborn 增強(qiáng)版的散點(diǎn)圖,一個(gè)聯(lián)合的點(diǎn),它顯示了兩個(gè)變量是相關(guān)的,并有著類(lèi)似地分布。

Python

data = pandas.DataFrame({"lengths": name_lengths, "ids": airlines["id"].astype(int)})
seaborn.jointplot(x="ids", y="lengths", data=data)
data = pandas.DataFrame({"lengths": name_lengths, "ids": airlines["id"].astype(int)})
seaborn.jointplot(x="ids", y="lengths", data=data)

上面的圖表明,兩個(gè)變量之間的相關(guān)性是不明確的——r 的平方值是低的。

靜態(tài) maps

我們的數(shù)據(jù)天然的適合繪圖-機(jī)場(chǎng)有經(jīng)度和緯度對(duì),對(duì)于出發(fā)和目的機(jī)場(chǎng)來(lái)說(shuō)也是。

第一張圖做的是顯示全世界的所有機(jī)場(chǎng)??梢杂脭U(kuò)展于 matplotlib 的  basemap  來(lái)做這個(gè)。這允許畫(huà)世界地圖和添加點(diǎn),而且很容易定制。

Python

# Import the basemap package
from mpl_toolkits.basemap import Basemap
# Create a map on which to draw.  We're using a mercator projection, and showing the whole world.
m = Basemap(projection='merc',llcrnrlat=-80,urcrnrlat=80,llcrnrlon=-180,urcrnrlon=180,lat_ts=20,resolution='c')
# Draw coastlines, and the edges of the map.
m.drawcoastlines()
m.drawmapboundary()
# Convert latitude and longitude to x and y coordinates
x, y = m(list(airports["longitude"].astype(float)), list(airports["latitude"].astype(float)))
# Use matplotlib to draw the points onto the map.
m.scatter(x,y,1,marker='o',color='red')
# Show the plot.
plt.show()
# Import the basemap package
from mpl_toolkits.basemapimport Basemap
# Create a map on which to draw.  We're using a mercator projection, and showing the whole world.
m = Basemap(projection='merc',llcrnrlat=-80,urcrnrlat=80,llcrnrlon=-180,urcrnrlon=180,lat_ts=20,resolution='c')
# Draw coastlines, and the edges of the map.
m.drawcoastlines()
m.drawmapboundary()
# Convert latitude and longitude to x and y coordinates
x, y = m(list(airports["longitude"].astype(float)), list(airports["latitude"].astype(float)))
# Use matplotlib to draw the points onto the map.
m.scatter(x,y,1,marker='o',color='red')
# Show the plot.
plt.show()

在上面的代碼中,首先用 mercator projection 畫(huà)一個(gè)世界地圖。墨卡托投影是將整個(gè)世界的繪圖投射到二位曲面。然后,在地圖上用紅點(diǎn)點(diǎn)畫(huà)機(jī)場(chǎng)。

上面地圖的問(wèn)題是找到每個(gè)機(jī)場(chǎng)在哪是困難的-他們就是在機(jī)場(chǎng)密度高的區(qū)域合并城一團(tuán)紅色斑點(diǎn)。

就像聚焦不清楚,有個(gè)交互制圖的庫(kù),folium,可以進(jìn)行放大地圖來(lái)幫助我們找到個(gè)別的機(jī)場(chǎng)。

Python

import folium
# Get a basic world map.
airports_map = folium.Map(location=[30, 0], zoom_start=2)
# Draw markers on the map.
for name, row in airports.iterrows():
    # For some reason, this one airport causes issues with the map.
    if row["name"] != "South Pole Station":
        airports_map.circle_marker(location=[row["latitude"], row["longitude"]], popup=row["name"])
# Create and show the map.
airports_map.create_map('airports.html')
airports_map
import folium
# Get a basic world map.
airports_map = folium.Map(location=[30, 0], zoom_start=2)
# Draw markers on the map.
for name, rowin airports.iterrows():
    # For some reason, this one airport causes issues with the map.
    if row["name"] != "South Pole Station":
        airports_map.circle_marker(location=[row["latitude"], row["longitude"]], popup=row["name"])
# Create and show the map.
airports_map.create_map('airports.html')
airports_map

Folium 使用 leaflet.js 來(lái)制作全交互式地圖。你可以點(diǎn)擊每一個(gè)機(jī)場(chǎng)在彈出框中看名字。在上邊顯示一個(gè)截屏,但是實(shí)際的地圖更令人印象深刻。Folium 也允許非常廣闊的修改選項(xiàng)來(lái)做更好的標(biāo)注,或者添加更多的東西到地圖上。

畫(huà)弧線(xiàn)

在地圖上看到所有的航空路線(xiàn)是很酷的,幸運(yùn)的是,我們可以使用 basemap 來(lái)做這件事。我們將畫(huà) 弧線(xiàn) 連接所有的機(jī)場(chǎng)出發(fā)地和目的地。每個(gè)弧線(xiàn)想展示一個(gè)段都航線(xiàn)的路徑。不幸的是,展示所有的線(xiàn)路又有太多的路由,這將會(huì)是一團(tuán)糟。替代,我們只現(xiàn)實(shí)前 3000 個(gè)路由。

Python

# Make a base map with a mercator projection.  Draw the coastlines.
m = Basemap(projection='merc',llcrnrlat=-80,urcrnrlat=80,llcrnrlon=-180,urcrnrlon=180,lat_ts=20,resolution='c')
m.drawcoastlines()
# Iterate through the first 3000 rows.
for name, row in routes[:3000].iterrows():
    try:
        # Get the source and dest airports.
        source = airports[airports["id"] == row["source_id"]].iloc[0]
        dest = airports[airports["id"] == row["dest_id"]].iloc[0]
        # Don't draw overly long routes.
        if abs(float(source["longitude"]) - float(dest["longitude"])) < 90:
            # Draw a great circle between source and dest airports.
            m.drawgreatcircle(float(source["longitude"]), float(source["latitude"]), float(dest["longitude"]), float(dest["latitude"]),linewidth=1,color='b')
    except (ValueError, IndexError):
        pass

# Show the map.
plt.show()
# Make a base map with a mercator projection.  Draw the coastlines.
m = Basemap(projection='merc',llcrnrlat=-80,urcrnrlat=80,llcrnrlon=-180,urcrnrlon=180,lat_ts=20,resolution='c')
m.drawcoastlines()
# Iterate through the first 3000 rows.
for name, rowin routes[:3000].iterrows():
    try:
        # Get the source and dest airports.
        source = airports[airports["id"] == row["source_id"]].iloc[0]
        dest = airports[airports["id"] == row["dest_id"]].iloc[0]
        # Don't draw overly long routes.
        if abs(float(source["longitude"]) - float(dest["longitude"])) < 90:
            # Draw a great circle between source and dest airports.
            m.drawgreatcircle(float(source["longitude"]), float(source["latitude"]), float(dest["longitude"]), float(dest["latitude"]),linewidth=1,color='b')
    except (ValueError, IndexError):
        pass
 
# Show the map.
plt.show()

上面的代碼將會(huì)畫(huà)一個(gè)地圖,然后再在地圖上畫(huà)線(xiàn)路。我們添加一了寫(xiě)過(guò)濾器來(lái)阻止過(guò)長(zhǎng)的干擾其他路由的長(zhǎng)路由。

畫(huà)網(wǎng)絡(luò)圖

我們將做的最終的探索是畫(huà)一個(gè)機(jī)場(chǎng)網(wǎng)絡(luò)圖。每個(gè)機(jī)場(chǎng)將會(huì)是網(wǎng)絡(luò)中的一個(gè)節(jié)點(diǎn),并且如果兩點(diǎn)之間有路由將劃出節(jié)點(diǎn)之間的連線(xiàn)。如果有多重路由,將添加線(xiàn)的權(quán)重,以顯示機(jī)場(chǎng)連接的更多。將使用 networkx 庫(kù)來(lái)做這個(gè)功能。

首先,計(jì)算機(jī)場(chǎng)之間連線(xiàn)的權(quán)重。

Python

# Initialize the weights dictionary.
weights = {}
# Keep track of keys that have been added once -- we only want edges with a weight of more than 1 to keep our network size manageable.
added_keys = []
# Iterate through each route.
for name, row in routes.iterrows():
    # Extract the source and dest airport ids.
    source = row["source_id"]
    dest = row["dest_id"]

    # Create a key for the weights dictionary.
    # This corresponds to one edge, and has the start and end of the route.
    key = "{0}_{1}".format(source, dest)
    # If the key is already in weights, increment the weight.
    if key in weights:
        weights[key] += 1
    # If the key is in added keys, initialize the key in the weights dictionary, with a weight of 2.
    elif key in added_keys:
        weights[key] = 2
    # If the key isn't in added_keys yet, append it.
    # This ensures that we aren't adding edges with a weight of 1.
    else:
        added_keys.append(key)
# Initialize the weights dictionary.
weights = {}
# Keep track of keys that have been added once -- we only want edges with a weight of more than 1 to keep our network size manageable.
added_keys = []
# Iterate through each route.
for name, rowin routes.iterrows():
    # Extract the source and dest airport ids.
    source = row["source_id"]
    dest = row["dest_id"]
 
    # Create a key for the weights dictionary.
    # This corresponds to one edge, and has the start and end of the route.
    key = "{0}_{1}".format(source, dest)
    # If the key is already in weights, increment the weight.
    if keyin weights:
        weights[key] += 1
    # If the key is in added keys, initialize the key in the weights dictionary, with a weight of 2.
    elif keyin added_keys:
        weights[key] = 2
    # If the key isn't in added_keys yet, append it.
    # This ensures that we aren't adding edges with a weight of 1.
    else:
        added_keys.append(key)

一旦上面的代碼運(yùn)行,這個(gè)權(quán)重字典就包含了每?jī)蓚€(gè)機(jī)場(chǎng)之間權(quán)重大于或等于 2 的連線(xiàn)。所以任何機(jī)場(chǎng)有兩個(gè)或者更多連接的路由將會(huì)顯示出來(lái)。

Python

# Import networkx and initialize the graph.
import networkx as nx
graph = nx.Graph()
# Keep track of added nodes in this set so we don't add twice.
nodes = set()
# Iterate through each edge.
for k, weight in weights.items():
    try:
        # Split the source and dest ids and convert to integers.
        source, dest = k.split("_")
        source, dest = [int(source), int(dest)]
        # Add the source if it isn't in the nodes.
        if source not in nodes:
            graph.add_node(source)
        # Add the dest if it isn't in the nodes.
        if dest not in nodes:
            graph.add_node(dest)
        # Add both source and dest to the nodes set.
        # Sets don't allow duplicates.
        nodes.add(source)
        nodes.add(dest)

        # Add the edge to the graph.
        graph.add_edge(source, dest, weight=weight)
    except (ValueError, IndexError):
        pass
pos=nx.spring_layout(graph)
# Draw the nodes and edges.
nx.draw_networkx_nodes(graph,pos, node_color='red', node_size=10, alpha=0.8)
nx.draw_networkx_edges(graph,pos,width=1.0,alpha=1)
# Show the plot.
plt.show()
# Import networkx and initialize the graph.
import networkxas nx
graph = nx.Graph()
# Keep track of added nodes in this set so we don't add twice.
nodes = set()
# Iterate through each edge.
for k, weightin weights.items():
    try:
        # Split the source and dest ids and convert to integers.
        source, dest = k.split("_")
        source, dest = [int(source), int(dest)]
        # Add the source if it isn't in the nodes.
        if sourcenot in nodes:
            graph.add_node(source)
        # Add the dest if it isn't in the nodes.
        if destnot in nodes:
            graph.add_node(dest)
        # Add both source and dest to the nodes set.
        # Sets don't allow duplicates.
        nodes.add(source)
        nodes.add(dest)
 
        # Add the edge to the graph.
        graph.add_edge(source, dest, weight=weight)
    except (ValueError, IndexError):
        pass
pos=nx.spring_layout(graph)
# Draw the nodes and edges.
nx.draw_networkx_nodes(graph,pos, node_color='red', node_size=10, alpha=0.8)
nx.draw_networkx_edges(graph,pos,width=1.0,alpha=1)
# Show the plot.
plt.show()

總結(jié)

有一個(gè)成長(zhǎng)的數(shù)據(jù)可視化的 Python 庫(kù),它可能會(huì)制作任意一種可視化。大多數(shù)庫(kù)基于 matplotlib 構(gòu)建的并且確保一些用例更簡(jiǎn)單。如果你想更深入的學(xué)習(xí)怎樣使用 matplotlib,seaborn 和其他工具來(lái)可視化數(shù)據(jù),在 這兒 檢出其他課程。 

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多

    午夜国产精品福利在线观看| 精品人妻一区二区三区四在线| 国产精品日本女优在线观看| 欧美日韩国产精品第五页| 国产一区二区三区色噜噜| 国产盗摄精品一区二区视频| 91欧美视频在线观看免费| 99热在线精品视频观看| 毛片在线观看免费日韩| 成人精品视频在线观看不卡| 老司机精品视频免费入口| 精品女同一区二区三区| 亚洲精品偷拍视频免费观看| 黄色日韩欧美在线观看| 日韩在线视频精品视频| 国产不卡在线免费观看视频| 在线观看视频日韩成人| 亚洲国产av一二三区| 国产男女激情在线视频| 中日韩美女黄色一级片| 欧美一区日韩一区日韩一区| 国产欧美日韩综合精品二区| 国产色偷丝袜麻豆亚洲| 女生更色还是男生更色| 日本成人中文字幕一区| 亚洲国产天堂av成人在线播放| 欧美乱妇日本乱码特黄大片| 亚洲淫片一区二区三区| 国产情侣激情在线对白| av在线免费播放一区二区| 日韩女优视频国产一区| 亚洲一区二区三区福利视频| 欧美熟妇喷浆一区二区| 色偷偷偷拍视频在线观看| 福利视频一区二区在线| 精品一区二区三区人妻视频| 精品老司机视频在线观看| 日韩精品成区中文字幕| 在线视频三区日本精品| 欧美日韩在线视频一区| 中文字幕人妻一区二区免费|