Unverified Commit 07bea5c1 authored by Kiryuu's avatar Kiryuu
Browse files

Finally finish this series

parent 0f0b0699
# Basemap 和 Seaborn
> 本关我们将学习 Basemap 和 Seaborn 这两个库。
https://www.educoder.net/shixuns/filjwkqy/challenges
## 第1关:Seaborn
### 任务描述
本关任务:编写一个绘制每个月销售总额的折线图。
### 相关知识
为了完成本关任务,你需要掌握:
1. matplotlib的缺点,
2. 如何使用seaborn进行可视化,并设置整体布局风格。
- 适用场景: 折线图适合二维的大数据集,尤其是那些趋势比单个数据点更重要的场合。它还适合多个二维数据集的比较。
#### 用Seaborn做可视化
即使 `matplotlib` 已经如此强大了,但是不得不承认它不支持的功能还有很多。总结如下:
- 2.0 之前的版本的默认配置样式绝对不是用户的最佳选择;
- `matplotlib` 的 API 比较底层。虽然可以实现复杂的统计数据可视化,但是通常都需要写大量的样板代码;
- `matplotlib` 不支持 `Pandas``DataFrame` 数据的可视化,必须先提取每个 `Series`
对于这些问题的终结者就是 `seaborn``Seaborn``Matplotlib` 的基础上开发了一套 API,为默认的图形样式和颜色设置提供了理智的选择,为常用的统计图形定义了许多简单的高级函数,并与 `Pandas DataFrame` 的功能有机结合。`Seaborn` 是在 `matplotlib` 的基础上进行了更高级的 API 封装,从而使得作图更加容易,在大多数情况下使用 `seaborn` 能做出很具有吸引力的图。
而使用 `matplotlib` 就能制作具有更多特色的图。应该把 `Seaborn` 视为 `matplotlib` 的补充,而不是替代物。同时它能高度兼容 `numpy``Pandas` 数据结构以及 `scipy``statsmodels` 等统计模式。
#### `Seaborn` 与 `Matplotlib`
下面用 `Matplotlib` 的经典图形样式和配色方案画一个简易的随机游走图。
```python
import matplotlib.pyplot as plt
plt.style.use('classic')
import numpy as np
import pandas as pd
rng = np.random.RandomState(0)
x = np.linspace(0, 10, 500)
y = np.cumsum(rng.randn(500, 6), 0)
plt.plot(x, y)
plt.legend('ABCDEF', ncol=2, loc='upper left')
plt.show()
```
![img/01.png](img/01.png)
在看看使用 `seaborn` 来实现。我们会发现,`Seaborn` 不仅有许多高级的画图功能,而且可以改写 `matplotlib` 的默认参数,从而用简单的 `matplotlib` 脚本获得更好的效果。可以用 `Seaborn``set()` 方法设置样式。
```python
import seaborn as sns
sns.set()
plt.plot(x, y)
plt.legend('ABCDEF', ncol=2, loc='upper left')
plt.savefig("T2.png")
plt.show()
```
![img/02.png](img/02.png)
上面应用了 `seaborn` 的默认样式。`seaborn` 有 5 个 `seaborn` 的主题,适用于不同的应用和人群偏好:
- `darkgrid` 黑色网格(默认);
- `whitegrid` 白色网格;
- `dark` 黑色背景;
- `white` 白色背景;
- `ticks` 应该是四周都有刻度线的白背景。
### 编程要求
本关的编程任务是补全右侧上部代码编辑区内的相应代码,根据输入文件路径读取文件,统计各个月销售总和,然后绘制折线图,需要应用 `seaborn` 设置默认样式,具体可视化要求如下:
- 折线图的 `figsize``(10, 10)`
- 文件名为 `Task1/img/T1.png`
- 具体要求请参见后续测试样例。
文件的部分数据如下:
![img/03.png](img/03.png)
**提示**:在绘制折线图时,需要对数据进行预处理,通过支付时间提取月份信息,再根据月份信息进行聚类,汇总每个月的实际金额,得到月销售总和。
请先仔细阅读右侧上部代码编辑区内给出的代码框架,再开始你的编程工作!
### 测试说明
平台会对你编写的代码进行测试,对比你输出的数值与实际正确的数值,只有所有数据全部计算正确才能进入下一关。
测试输入:
`无测试输入`
预期输出:
`生成图片与预期图片一致`
----
开始你的任务吧,祝你成功!
### 答案
```python
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
def student(input_data):
# ********* Begin *********#
sns.set()
plt.figure(figsize=(10,10))
a=pd.read_csv(input_data)
a["month"]=a["支付时间"].map(lambda x:int(x.split("/")[1]))
d=a["实际金额"].groupby(a["month"]).sum()
d=d.sort_index(ascending=False)
plt.plot(d)
plt.savefig("Task1/img/T1.png")
plt.show()
# ********* End *********#
```
## 第2关:Seaborn图形介绍
### 任务描述
本关任务:编写一个使用 `Seaborn` 来绘制散点图的程序。
### 相关知识
`Seaborn` 的主要思想是用高级命令为统计数据探索和统计模型拟合创建各种图形,下面将介绍一些 `Seaborn` 中的数据集和图形类型。
虽然所有这些图形都可以用 `Matplotlib` 命令实现(其实 `Matplotlib` 就是 `Seaborn` 的底层),但是用 `Seaborn API` 会更方便。
#### 频次直方图、KDE 和密度图
在进行统计数据可视化时,我们通常想要的就是频次直方图和多变量的联合分布图。在 `Matplotlib` 里面我们已经见过,相对比较简单:
```python
data = np.random.multivariate_normal([0, 0], [[5, 2], [2, 2]], size=2000)
data = pd.DataFrame(data, columns=['x', 'y'])
for col in 'xy':
plt.hist(data[col], normed=True, alpha=0.5)
```
![img/04.png](img/04.png)
除了频次直方图,我们还可以用KDE获取变量分布的平滑估计。在 `seaborn` 通过 `sns.kdeplot` 来实现:
```python
for col in 'xy':
sns.kdeplot(data[col], shade=True)
```
![img/05.png](img/05.png)
使用 `distplot` 可以将频次直方图和 KDE 结合起来:
```python
sns.distplot(data['x'])
sns.distplot(data['y'])
```
![img/06.png](img/06.png)
如果向 `kdeplot` 输入的是二维数据集,那么就可以获得一个二维数据可视化图:`sns.kdeplot(data)`
![img/07.png](img/07.png)
#### 矩阵图
当我们需要对多维数据进行可视化是,最终都要使用矩阵图,矩阵图对于探索多维数据不同维度间的相关性非常有效。
下面将用鸢尾花数据集来演示,其中有三种鸢尾花的花瓣与花萼数据:
```python
data = pd.read_csv("iris.csv")
sns.pairplot(data,hue="species") #hue 选择分类列
```
![img/08.png](img/08.png)
#### 分面频次直方图
有时观察数据最好的方法就是借助数据子集的频次直方图,`Seaborn``FacetGrid` 函数让这件事变得非常简单。
来看看某个餐厅统计的服务员收取小费的数据:
```python
tips = pd.read_csv('tips.csv')
g = sns.FacetGrid(tips, col="time", row="smoker")
g = g.map(plt.hist, "total_bill", color="r")
```
![img/09.png](img/09.png)
#### 条形图
对于时间序列数据可以使用 `sns.factorplot` 画出条形图,下面将使用行星数据来演示:
```python
planets = pd.read_csv('planets.csv')
with sns.axes_style('white'):
g = sns.factorplot("year", data=planets, aspect=2,
kind="count", color='steelblue')
g.set_xticklabels(step=5)
```
![img/10.png](img/10.png)
还可以对用不同方法发现行星的数量:
```python
with sns.axes_style('white'):
g = sns.factorplot("year", data=planets, aspect=4.0, kind='count',
hue='method', order=range(2001, 2015))
g.set_ylabels('Number of Planets Discovered')
```
![img/11.png](img/11.png)
#### 折线图
`seaborn` 绘制折线图使用 `lineplot` 函数,该函数所传数据必须为一个 `pandas` 数组,这一点跟 `matplotlib` 里有较大的区别,并且一开始使用较为复杂。
首先 `sns.lineplot` 里有几个参数值得注意:
- `x:plot` 图的 x 轴 `label`
- `y:plot` 图的 y 轴 `label`
- `ci`: 与估计器聚合时绘制的置信区间的大小;
- `data`: 所传入的 `pandas` 数组。
```python
x = np.linspace(100, 50, 6)
y = np.array([0.194173876, 0.161086478, 0.138896531, 0.129826697, 0.133716787, 0.152458326])
summary = []
for i in range(6):
x_t = x[i]
y_t = y[i]
summary.append([x_t, y_t])
data =pd.DataFrame(summary )
sns.lineplot(x=0,y=1,ci=None,data=data)
```
![img/12.png](img/12.png)
### 编程要求
本关的编程任务是补全右侧上部代码编辑区内的相应代码,根据输入的文件路径读取文件并使用 `factorplot` 绘制关于 `smoker` 字段的条形图以统计吸烟和不吸烟的人数,具体可视化要求如下:
- 设置 `Seaborn` 样式为 `white`
- 设置颜色为 `steelblue`
- 设置 `kind``count`
- 图形的 `figsize``(10, 10)`
- 文件名为 `Task2/img/T1.png`
- 具体要求请参见后续测试样例。
请先仔细阅读右侧上部代码编辑区内给出的代码框架,再开始你的编程工作!
### 测试说明
平台会对你编写的代码进行测试,对比你输出的数值与实际正确的数值,只有所有数据全部计算正确才能进入下一关。
测试输入:
`无测试输入`
预期输出:
`生成图片与预期图片一致`
----
开始你的任务吧,祝你成功!
### 答案
```python
import matplotlib
matplotlib.use("Agg")
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings("ignore")
def student(file_path):
# ********* Begin *********#
    plt.figure(figsize=(10,10))
    tips = pd.read_csv(file_path)
    with sns.axes_style('white'):
        sns.factorplot("smoker", data=tips, kind="count",  color='steelblue')
    plt.savefig("Task2/img/T1.png")
    plt.show()
# ********* End *********#
```
## 第3关:Basemap
### 任务描述
本关任务:以中国地图为背景绘制散点图。
### 相关知识
`Basemap``Matplotlib` 的一个子包,负责地图绘制。在数据可视化过程中,我们常需要将数据在地图上画出来。
比如说我们在地图上画出城市人口,飞机航线,军事基地,矿藏分布等等。这样的地理绘图有助于读者理解空间相关的信息。
- 适用场景:适用于有空间位置的数据集。
### 安装和使用
相对于其他工具 `Basemap` 用起来有点笨重,就算做点儿简单的可视化图也需要花费比预期更长的时间。
在处理比较复杂的地图可视化任务时,更现代的解决方案可能会更适用一些,比如 leaflet、Google Maps API。然而,`Basemap` 符合 Python 用户的使用习惯。
`basemap` 并没有集成到 `matplotlib` 中,需要我们手动安装,`basemap` 安装起来很简单,这里就不在说明。
安装并导入 `basemap` 工具箱后,只需要用几行代码就可以画出地理图:
```python
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap#导入工具包
plt.figure(figsize=(8, 8))
m = Basemap(projection='ortho', resolution=None, lat_0=50, lon_0=-100)
m.bluemarble(scale=0.5)
plt.show()
```
![img/13.png](img/13.png)
运用 `Basemap` 函数我们可以在绘图区域中绘制地理信息相关的图像,当参数 ` projection` 的值为 `'ortho'` 时,我们将得到一个如上图所示的地球仪截面。
将参数 `projection` 的值设置为 `lcc` 时,我们可以通过经纬度设置来得到某一区域的局部地图:
```python
fig = plt.figure(figsize=(8, 8))
m = Basemap(projection='lcc', resolution=None,width=8E6,height=8E6,lat_0=45, lon_0=-100,)m.etopo(scale=0.5, alpha=0.5)
# 将经纬度映射为 (x, y) 坐标,用于绘制图像
x, y = m(-122.3, 47.6)
plt.plot(x, y, 'ok', markersize=5)
plt.text(x, y, ' Seattle', fontsize=12)
```
![img/14.png](img/14.png)
这里使用了两个额外参数,它们用来表示地图中心的纬度(`lat_0`)和经度(`lon_0`)。
#### 地图投影
由于不可能把地表完美反映到二维平面上,所有的地图都是各种各样扭曲的产物,把这些扭曲的产物抹平到平面坐标系的过程,称为投影。
`Basemap` 提供了几十种不同的投影的实现。
投影简写-全称对照:
| 简写 | 全称 |
| ------- | --------------------------------- |
| cyl | Cylindrical Equidistant |
| merc | Mercator |
| tmerc | Transverse Mercator |
| omerc | Oblique Mercator |
| mill | Miller Cylindrical |
| gall | Gall Stereographic Cylindrical |
| cea | Cylindrical Equal Area |
| lcc | Lambert Conformal |
| laea | Lambert Azimuthal Equal Area |
| nplaea | North-Polar Lambert Azimuthal |
| splaea | South-Polar Lambert Azimuthal |
| eqdc | Equidistant Conic |
| aeqd | Azimuthal Equidistant |
| npaeqd | North-Polar Azimuthal Equidistant |
| spaeqd | South-Polar Azimuthal Equidistant |
| aea | Albers Equal Area |
| stere | Stereographic |
| npstere | North-Polar Stereographic |
| spstere | South-Polar Stereographic |
| cass | Cassini-Soldner |
| poly | Polyconic |
| ortho | Orthographic |
| geos | Geostationary |
| nsper | Near-Sided Perspective |
| sinu | Sinusoidal |
| moll | Mollweide |
| hammer | Hammer |
| robin | Robinson |
| kav7 | Kavrayskiy VII |
| eck4 | Eckert IV |
| vandg | van der Grinten |
| mbtfpq | McBryde-Thomas Flat-Polar Quartic |
| gnom | Gnomonic |
| rotpole | Rotated Pole |
下面我们对一常用的投影进行简单的演示。定义一个可以画带经纬线地图的简便方法:
```python
def draw_map(m, scale=0.2):
# 画地貌晕渲图
m.shadedrelief(scale=scale)
# 用字典表示经纬度
lats = m.drawparallels(np.linspace(-90, 90, 13))
lons = m.drawmeridians(np.linspace(-180, 180, 13))
# 字典的键是plt.Line2D示例
lat_lines = chain(*(tup[1][0] for tup in lats.items()))
lon_lines = chain(*(tup[1][0] for tup in lons.items()))
all_lines = chain(lat_lines, lon_lines)
# 用循环将所有线设置成需要的样式
for line in all_lines:
line.set(linestyle='-', alpha=0.3, color='w')
```
圆柱投影是最简单的地图投影类型,纬度线与经度线分别映射成水平线与竖直线。
采用这种投影类型的话,赤道区域的显示效果非常好,但是南北极附近的区域就会严重变形。
```python
fig = plt.figure(figsize=(8, 6), edgecolor='w')
m = Basemap(projection='cyl', resolution=None,
llcrnrlat=-90, urcrnrlat=90,
llcrnrlon=-180, urcrnrlon=180, )
draw_map(m)
```
![img/15.png](img/15.png)
这里basemap参数设置了左下角(`llcrnr`)和右上角(`urcrnr`)纬度(`lat`)和经度(`lon`)。不同的投影都有各种的优劣,大家之后可以多多尝试。
#### 地图背景
`basemap` 程序包中有许多实用的函数,可以画出各种地形的轮廓,如陆地、海洋、湖泊、河流、各国的政治分界线。
常用画图函数:
| 函数 | 说明 |
| ----------------- | -------------------------------------------------------- |
| drawcoastlines() | 绘制大陆海岸线 |
| drawlsmask() | 为陆地与海洋设置填充色,从而可以在陆地或海洋投影其他图像 |
| drawmapboundary() | 绘制地图边界,包括为海洋填充颜色 |
| drawrivers() | 绘制河流 |
| fillcontinents() | 用一种颜色填充大陆,用另一种颜色填充湖泊(可选) |
| drawcountries() | 绘制国界线 |
| drawstates() | 绘制美国州界线 |
| drawcounties() | 绘制美国县界线 |
| drawgreatcircle() | 在两点之间绘制一个大圆 |
| drawparallels() | 绘制纬线 |
| drawmeridians() | 绘制经线 |
| drawmapscale() | 在地图上绘制一个线性比例尺 |
| bluemarble() | 绘制NASA 蓝色弹珠地球投影 |
| shadedrelief() | 在地图上绘制地貌晕渲图 |
| etopo() | 在地图上绘制地形晕渲图 |
| warpimage() | 将用户提供的图像投影到地图上 |
如果要使用边界特征,就必须设置分辨率。通过 `resolution` 来设置分辨率,取值为 `c`(原始分辨率)、`l`(低分辨率)、`i`(中分辨率)、`h`(高分辨率)、`f`(全画质分辨率)。
来看看两种不同分辨率的绘制效果:
```python
fig, ax = plt.subplots(1, 2, figsize=(12, 8))
for i, res in enumerate(['l', 'h']):
m = Basemap(projection='gnom', lat_0=57.3, lon_0=-6.2,
width=90000, height=120000, resolution=res, ax=ax[i])
m.fillcontinents(color="#FFDDCC", lake_color='#DDEEFF')
m.drawmapboundary(fill_color="#DDEEFF")
m.drawcoastlines()
ax[i].set_title("resolution='{0}'".format(res));
plt.show()
```
![img/16.png](img/16.png)
可以看出低分辨率不适合这个缩放,低分辨率适合呈现全局视角,而且加载速度比高分辨率更快。要呈现某一视角的适合,最好先从一个能快速呈现的分辨率开始,然后不断提高分辨率直到满意为止。
#### 在地图上画数据
`basemap` 还可以以地图为背景,在这上面画各种数据。`basemap` 实例中许多方法都是与地图有关的函数。这些函数与标准 `matplotlib` 函数的用法类似,只是多了一个参数 `latlon`。如果设置为 `true` 表示使用原来的经纬度坐标,不使用投影 (x, y) 坐标。
示例如下:
```python
import pandas as pd
cities = pd.read_csv('california_cities.csv')
# 提取我们感兴趣的数据
lat = cities['latd'].values
lon = cities['longd'].values
population = cities['population_total'].values
area = cities['area_total_km2'].values
# 1. 绘制地图背景
fig = plt.figure(figsize=(8, 8))
m = Basemap(projection='lcc', resolution='h',
lat_0=37.5, lon_0=-119,
width=1E6, height=1.2E6)
m.shadedrelief()
m.drawcoastlines(color='gray')
m.drawcountries(color='gray')
m.drawstates(color='gray')
# 2. 绘制城市数据的散点图,其中颜色反映人口
# 尺寸反映面积
m.scatter(lon, lat, latlon=True,
c=np.log10(population), s=area,
cmap='Reds', alpha=0.5)
# 3. 创建颜色条和图例
plt.colorbar(label=r'$\log_{10}({\rm population})$')
plt.clim(3, 7)
# 使用虚拟的点生成图例
for a in [100, 300, 500]:
plt.scatter([], [], c='k', alpha=0.5, s=a,
label=str(a) + ' km$^2$')
plt.legend(scatterpoints=1, frameon=False,
labelspacing=1, loc='lower left');
```
![img/17.png](img/17.png)
### 编程要求
本关的编程任务是补全右侧上部代码编辑区内的相应代码,中国地图已经绘制好,只需要读取输入参数 `img_path` 作为绘图背景,`x / y`为散点图数据,需要设置散点图的参数为标记设置为 `o`,大小为 150,具体可视化要求如下:
- 图形的 `figsize``(10, 10)`
- 图形保存到 `Task3/img/T1.png`
- 具体要求请参见后续测试样例。
请先仔细阅读右侧上部代码编辑区内给出的代码框架,再开始你的编程工作!
### 测试说明
平台会对你编写的代码进行测试,对比你输出的数值与实际正确的数值,只有所有数据全部计算正确才能进入下一关。
测试输入:
`无测试输入`
预期输出:
`生产图片与预期图片一致`
----
开始你的任务吧,祝你成功!
### 答案
```python