Exploration de l'ensemble de données
Avant de discuter de la visualisation des données, jetons un coup d'œil rapide à l'ensemble de données avec lequel nous allons travailler. Les données que nous allons utiliser proviennent d'openlights. Nous allons utiliser l'ensemble de données sur les itinéraires, l'ensemble de données sur les aéroports, l'ensemble de données sur les compagnies aériennes. Parmi eux, chaque ligne de données de trajet correspond à la trajectoire de vol entre deux aéroports ; chaque ligne de données d'aéroport correspond à un aéroport dans le monde et fournit des informations pertinentes sur chaque compagnie aérienne ;
Nous lisons d'abord les données :
# 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"]
Ces données n'ont pas le premier élément de la colonne, nous ajoutons donc le premier élément de la colonne en attribuant l'attribut de colonne. Nous souhaitons lire chaque colonne sous forme de chaîne, car cela simplifie l'étape suivante de comparaison de différentes trames de données en utilisant les identifiants de ligne comme correspondances. Nous y parvenons en définissant la valeur de l'attribut dtype lors de la lecture des données.
Donc, avant cela, nous devons effectuer un travail de nettoyage des données.
routes = routes[routes["airline_id"] != "//N"]
Cette ligne de commande garantit que nous ne contenons que des données numériques dans la colonne Airlines_id.
Créer un histogramme
Maintenant que nous comprenons la structure des données, nous pouvons commencer à dessiner des points pour continuer à explorer ce problème. Tout d'abord, nous utiliserons l'outil matplotlib. Matplotlib est une bibliothèque de traçage de niveau relativement bas dans la pile Python, elle nécessite donc plus de commandes que les autres bibliothèques d'outils pour créer une belle courbe. D'un autre côté, vous pouvez utiliser matplotlib pour créer presque n'importe quelle courbe car il est très flexible, et le prix de la flexibilité est qu'il est très difficile à utiliser.
Nous montrons d'abord la répartition de la longueur des itinéraires des différentes compagnies aériennes en créant un histogramme. Un histogramme divise les longueurs de tous les itinéraires en différentes plages de valeurs, puis compte les itinéraires qui se situent dans les différentes plages de valeurs. Cela nous permet de savoir quelles compagnies aériennes ont des itinéraires longs et quelles compagnies aériennes ont des itinéraires courts.
Pour y parvenir, nous devons d'abord calculer la longueur de l'itinéraire. La première étape consiste à utiliser la formule de distance. Nous utiliserons la formule de distance cosinus demi-sinus pour calculer la distance entre deux points représentés par la longitude et la latitude.
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
Ensuite, nous pouvons utiliser une fonction pour calculer la distance aller simple entre l'aéroport d'origine et l'aéroport de destination. Nous devons obtenir le source_id et le dest_id correspondant à la trame de données de l'aéroport à partir de la trame de données d'itinéraire, puis les faire correspondre avec la colonne id de l'ensemble de données de l'aéroport. Il nous suffit ensuite de calculer cette fonction :
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
. Si les colonnes source_id et dest_id s'il n'y a pas de valeur valide, alors cette fonction signalera une erreur. Par conséquent, nous devons ajouter un module try/catch pour détecter cette situation invalide.
***, nous allons utiliser des pandas pour appliquer la fonction de calcul de distance au bloc de données des itinéraires. Cela nous donnera une séquence pandas contenant toutes les longueurs de route en kilomètres.
route_lengths = routes.apply(calc_dist, axis=1)
Maintenant que nous avons la séquence de distances d'itinéraire, nous allons créer un histogramme qui catégorisera les données dans la plage correspondante, puis compter le nombre d'itinéraires entrant dans chacun plage différente :
import matplotlib.pyplot as plt %matplotlib inline plt.hist(route_lengths, bins=20)
Nous utilisons import matplotlib.pyplot as plt pour importer la fonction de tracé de points matplotlib. Ensuite, nous avons utilisé %matplotlib en ligne pour configurer matplotlib afin qu'il dessine des points dans le notebook ipython. Enfin, nous avons utilisé plt.hist(route_lengths, bins=20) pour obtenir un histogramme. Comme nous l’avons vu, les compagnies aériennes ont tendance à exploiter des liaisons court-courriers proches les unes des autres plutôt que des liaisons long-courriers éloignées.
Utilisation de seaborn
Nous pouvons utiliser seaborn pour effectuer un traçage de points similaire. seaborn est une bibliothèque de haut niveau pour Python. Seaborn est construit sur la base de matplotlib et effectue certains types de traçage, qui sont souvent liés à de simples travaux statistiques. Nous pouvons utiliser la fonction distplot pour tracer un histogramme basé sur une attente de densité de probabilité de base. L'espérance de densité d'un noyau est une courbe - essentiellement une courbe qui est plus lisse qu'un histogramme et dont les règles sont plus faciles à voir.
import seaborn seaborn.distplot(route_lengths, bins=20)
seaborn a également un plus beau style par défaut. seaborn ne contient pas de version correspondant à chaque version de matplotlib, mais c'est en effet un bon outil de dessin de points rapide, et comparé au graphique par défaut de matplotlib, il peut mieux nous aider à comprendre la signification des données. Si vous souhaitez faire un travail statistique plus approfondi, seaborn est également une bonne bibliothèque.
Graphique à barres
柱状图也虽然很好,但是有时候我们会需要航空公司的平均路线长度。这时候我们可以使用条形图--每条航线都会有一个单独的状态条,显示航空公司航线 的平均长度。从中我们可以看出哪家是国内航空公司哪家是国际航空公司。我们可以使用pandas,一个python的数据分析库,来酸楚每个航空公司的平 均航线长度。
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)
我们首先用航线长度和航空公司的id来搭建一个新的数据框架。我们基于airline_id把route_length_df拆分成组,为每个航空 公司建立一个大体的数据框架。然后我们调用pandas的aggregate函数来获取航空公司数据框架中长度列的均值,然后把每个获取到的值重组到一个 新的数据模型里。之后把数据模型进行排序,这样就使得拥有最多航线的航空公司拍到了前面。
这样就可以使用matplotlib把结果画出来。
plt.bar(range(airline_route_lengths.shape[0]), airline_route_lengths["length"])
Matplotlib的plt.bar方法根据每个数据模型的航空公司平均航线长度(airline_route_lengths["length"])来做图。
问题是我们想看出哪家航空公司拥有的航线长度是什么并不容易。为了解决这个问题,我们需要能够看到坐标轴标签。这有点难,毕竟有这么多的航空公司。 一个能使问题变得简单的方法是使图表具有交互性,这样能实现放大跟缩小来查看轴标签。我们可以使用bokeh库来实现这个--它能便捷的实现交互性,作出 可缩放的图表。
要使用booked,我们需要先对数据进行预处理:
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])
上面的代码会获取airline_route_lengths中每列的名字,然后添加到name列上,这里存贮着每个航空公司的名字。我们也添加到id列上以实现查找(apply函数不传index)。
***,我们重置索引序列以得到所有的特殊值。没有这一步,Bokeh 无法正常运行。
现在,我们可以继续说图表问题:
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 创建背景虚化,在 iPython 的 notebook 里画出图。然后,使用数据帧和特定序列制作条形图。***,显示功能会显示出该图。
这个图实际上不是一个图像--它是一个 JavaScript 插件。因此,我们在下面展示的是一幅屏幕截图,而不是真实的表格。
有了它,我们可以放大,看哪一趟航班的飞行路线最长。上面的图像让这些表格看起来挤在了一起,但放大以后,看起来就方便多了。
水平条形图
Pygal 是一个能快速制作出有吸引力表格的数据分析库。我们可以用它来按长度分解路由。首先把我们的路由分成短、中、长三个距离,并在 route_lengths 里计算出它们各占的百分比。
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)
然后我们可以在 Pygal 的水平条形图里把每一个都绘成条形图:
import pygal from IPython.display import SVG chart = pygal.HorizontalBar() chart.title = 'Long, medium, and short routes' chart.add('Long', long_routes * 100) chart.add('Medium', medium_routes * 100) chart.add('Short', short_routes * 100) chart.render_to_file('routes.svg') SVG(filename='routes.svg')
首先,我们使用 pandasapplymethod 计算每个名称的长度。它将找到每个航空公司的名字字符的数量。然后,我们使用 matplotlib 做一个散点图来比较航空 id 的长度。当我们绘制时,我们把 theidcolumn of airlines 转换为整数类型。如果我们不这样做是行不通的,因为它需要在 x 轴上的数值。我们可以看到不少的长名字都出现在早先的 id 中。这可能意味着航空公司在成立前往往有较长的名字。
我们可以使用 seaborn 验证这个直觉。Seaborn 增强版的散点图,一个联合的点,它显示了两个变量是相关的,并有着类似地分布。
data = pandas.DataFrame({"lengths": name_lengths, "ids": airlines["id"].astype(int)})
seaborn.jointplot(x="ids", y="lengths", data=data)
画弧线
在地图上看到所有的航空路线是很酷的,幸运的是,我们可以使用 basemap 来做这件事。我们将画弧线连接所有的机场出发地和目的地。每个弧线想展示一个段都航线的路径。不幸的是,展示所有的线路又有太多的路由,这将会是一团糟。 替代,我们只现实前 3000 个路由。
# 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()
我们将做的最终的探索是画一个机场网络图。每个机场将会是网络中的一个节点,并且如果两点之间有路由将划出节点之间的连线。如果有多重路由,将添加线的权重,以显示机场连接的更多。将使用 networkx 库来做这个功能。
首先,计算机场之间连线的权重。
# 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)
一旦上面的代码运行,这个权重字典就包含了每两个机场之间权重大于或等于 2 的连线。所以任何机场有两个或者更多连接的路由将会显示出来。
# 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()
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!