python数据可视化基础入门教程

不要眼眶一红,就觉得人间不值得,散伙是人间常态,你我怎能例外

Posted by yishuifengxiao on 2021-05-25

一 pandas

Pandas 一个强大的分析结构化数据的工具集,基础是 Numpy(提供高性能的矩阵运算)。

Pandas 可以从各种文件格式比如 CSV、JSON、SQL、Microsoft Excel 导入数据。

Pandas 可以对各种数据进行运算操作,比如归并、再成形、选择,还有数据清洗和数据加工特征。

Pandas 应用:

Pandas 的主要数据结构是 Series (一维数据)与 DataFrame(二维数据),这两种数据结构足以处理金融、统计、社会科学、工程等领域里的大多数典型用例。

数据结构

Series 是一种类似于一维数组的对象,它由一组数据(各种Numpy数据类型)以及一组与之相关的数据标签(即索引)组成。

DataFrame 是一个表格型的数据结构,它含有一组有序的列,每列可以是不同的值类型(数值、字符串、布尔型值)。DataFrame 既有行索引也有列索引,它可以被看做由 Series 组成的字典(共同用一个索引)。

1.1 pandas安装

导入 pandas 一般使用别名 pd 来代替:

1
import pandas as pd

查看 pandas 版本

1
2
3
4
import pandas as pd

# 查看版本
print(pd.__version__)

运行结果为

1
2
3
4
import sys; print('Python %s on %s' % (sys.version, sys.platform))
sys.path.extend(['F:\\python', 'F:/python'])
PyDev console: starting.
Python 3.9.5 (tags/v3.9.5:0a7dcbd, May 3 2021, 17:27:52) [MSC v.1928 64 bit (AMD64)] on win32

1.2 数据结构 - Series

Pandas Series 类似表格中等一个列(column),类似于一维数组,可以保存任何数据类型 Series 由索引(index)和列组成,函数如下:

1
pandas.Series( data, index, dtype, name, copy)

参数说明:

  • data:一组数据(ndarray 类型)。
  • index:数据索引标签,如果不指定,默认从 0 开始。
  • dtype:数据类型,默认会自己判断。
  • name:设置名称。
  • copy:拷贝数据,默认为 False。

1.2.1 基本使用

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
import pandas as pd

a = [1, 2, 3]

myvar = pd.Series(a)

# 打印出数据
print(myvar)
print("\n------------\n")
# 如果没有指定索引,索引值就从 0 开始,我们可以根据索引值读取数据
print(myvar[1])
print("\n------------\n")
print(myvar[2])

运行结果如下

1
2
3
4
5
6
7
8
0    1
1 2
2 3
dtype: int64
------------
2
------------
3

1.2.2 字典方式创建

1
2
3
4
5
6
7
import pandas as pd

sites = {1: "Google", 2: "Runoob", 3: "Wiki"}

myvar = pd.Series(sites)

print(myvar)

运行结果如下

1
2
3
4
1    Google
2 Runoob
3 Wiki
dtype: object

从上可知,字典的 key 变成了索引值。如果我们只需要字典中的一部分数据,只需要指定需要数据的索引即可,如下实例:

1
2
3
4
5
6
7
import pandas as pd

sites = {1: "Google", 2: "Runoob", 3: "Wiki"}

myvar = pd.Series(sites, index=[1, 2])

print(myvar)

运行结果如下

1
2
3
1    Google
2 Runoob
dtype: object

注意在上述结果中,第三项数据丢失

1.2.3 设置 Series 名称参数

1
2
3
4
5
6
7
import pandas as pd

sites = {1: "Google", 2: "Runoob", 3: "Wiki"}

myvar = pd.Series(sites, index=[1, 2], name="RUNOOB-Series-TEST")

print(myvar)

运行结果如下

1
2
3
1    Google
2 Runoob
Name: RUNOOB-Series-TEST, dtype: object

注意在上述结果中,第三项数据丢失

若修改索引标签,代码如下

1
2
3
4
5
6
7
import pandas as pd

sites = {1: "Google", 2: "Runoob", 3: "Wiki"}

myvar = pd.Series(sites, index=[1, 2, 3, 4], name="RUNOOB-Series-TEST")

print(myvar)

得到的运行结果如下

1
2
3
4
5
1    Google
2 Runoob
3 Wiki
4 NaN
Name: RUNOOB-Series-TEST, dtype: object

1.3 数据结构 - DataFrame

DataFrame 是一个表格型的数据结构,它含有一组有序的列,每列可以是不同的值类型(数值、字符串、布尔型值)。DataFrame 既有行索引也有列索引,它可以被看做由 Series 组成的字典(共同用一个索引)

DataFrame 构造方法如下:

1
pandas.DataFrame( data, index, columns, dtype, copy)

参数说明:

  • data:一组数据(ndarray、series, map, lists, dict 等类型)。
  • index:索引值,或者可以称为行标签。(==Y轴==)
  • columns:列标签,默认为 RangeIndex (0, 1, 2, …, n) 。(==X轴==)
  • dtype:数据类型。
  • copy:拷贝数据,默认为 False。

1.3.1 基本使用

1
2
3
4
5
6
7
import pandas as pd

data = [['Google', 10], ['Runoob', 12], ['Wiki', 13]]

df = pd.DataFrame(data, columns=['Site', 'Age'], dtype=float)

print(df)

运行结果如下

1
2
3
4
     Site   Age
0 Google 10.0
1 Runoob 12.0
2 Wiki 13.0

注意,在创建时列标签必须与数据项保持一致,否则会发生异常

对于以下的代码

1
2
3
4
5
6
7
import pandas as pd

data = [['Google', 10], ['Runoob', 12], ['Wiki', 13]]

df = pd.DataFrame(data, columns=['Site', 'Age', 'name'], dtype=float) # 这里是第5行

print(df)

运行结果如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Traceback (most recent call last):
File "F:\python\venv\lib\site-packages\pandas\core\internals\construction.py", line 568, in _list_to_arrays
columns = _validate_or_indexify_columns(content, columns)
File "F:\python\venv\lib\site-packages\pandas\core\internals\construction.py", line 692, in _validate_or_indexify_columns
raise AssertionError(
AssertionError: 3 columns passed, passed data had 2 columns
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "C:\Program Files\JetBrains\PyCharm 2021.1.1\plugins\python\helpers\pydev\_pydev_bundle\pydev_umd.py", line 197, in runfile
pydev_imports.execfile(filename, global_vars, local_vars) # execute the script
File "C:\Program Files\JetBrains\PyCharm 2021.1.1\plugins\python\helpers\pydev\_pydev_imps\_pydev_execfile.py", line 18, in execfile
exec(compile(contents+"\n", file, 'exec'), glob, loc)
File "F:/python/PandasDemo.py", line 5, in <module>
df = pd.DataFrame(data, columns=['Site', 'Age', 'name'], dtype=float)
File "F:\python\venv\lib\site-packages\pandas\core\frame.py", line 570, in __init__
arrays, columns = to_arrays(data, columns, dtype=dtype)
File "F:\python\venv\lib\site-packages\pandas\core\internals\construction.py", line 528, in to_arrays
return _list_to_arrays(data, columns, coerce_float=coerce_float, dtype=dtype)
File "F:\python\venv\lib\site-packages\pandas\core\internals\construction.py", line 571, in _list_to_arrays
raise ValueError(e) from e
ValueError: 3 columns passed, passed data had 2 columns

1.3.2 使用 ndarrays 创建

使用 ndarrays 创建,ndarray 的长度必须相同, 如果传递了 index,则索引的长度应等于数组的长度。如果没有传递索引,则默认情况下,索引将是range(n),其中n是数组长度

对于以下代码

1
2
3
4
5
6
7
import pandas as pd

data = {'Site': ['Google', 'Runoob', 'Wiki'], 'Age': [10, 12, 13]}

df = pd.DataFrame(data)

print(df)

运行结果如下

1
2
3
4
     Site  Age
0 Google 10
1 Runoob 12
2 Wiki 13

注意:这里的每一项的数据的个数必须一致,否则会发生问题

1.3.3 使用字典创建

1
2
3
4
5
6
7
import pandas as pd

data = [{'a': 1, 'b': 2}, {'a': 5, 'b': 10, 'c': 20}]

df = pd.DataFrame(data)

print(df)

运行结果如下

1
2
3
   a   b     c
0 1 2 NaN
1 5 10 20.0

没有对应的部分数据为 NaN

1.3.4 数据获取

Pandas 可以使用 loc 属性返回指定行的数据,如果没有设置索引,第一行索引为 0,第二行索引为 1,以此类推:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import pandas as pd

data = {
"calories": [420, 380, 390],
"duration": [50, 40, 45]
}

# 数据载入到 DataFrame 对象
df = pd.DataFrame(data)

print(df)
print("\n------------\n")
# 返回第一行
print(df.loc[0])
print("\n------------\n")
# 返回第二行
print(df.loc[1])

运行结果如下

1
2
3
4
5
6
7
8
9
10
11
12
   calories  duration
0 420 50
1 380 40
2 390 45
------------
calories 420
duration 50
Name: 0, dtype: int64
------------
calories 380
duration 40
Name: 1, dtype: int64

注意:返回结果其实就是一个 Pandas Series 数据。


也可以返回多行数据,使用 [[ … ]] 格式, 为各行的索引,以逗号隔开:

1
2
3
4
5
6
7
8
9
10
11
12
import pandas as pd

data = {
"calories": [420, 380, 390],
"duration": [50, 40, 45]
}

# 数据载入到 DataFrame 对象
df = pd.DataFrame(data)

# 返回第一行和第二行
print(df.loc[[0, 1]])

运行结果为

1
2
3
   calories  duration
0 420 50
1 380 40

指定索引值

1
2
3
4
5
6
7
8
9
10
11
12
13
import pandas as pd

data = {
"calories": [420, 380, 390],
"duration": [50, 40, 45]
}

df = pd.DataFrame(data, index=["day1", "day2", "day3"])

print(df)
print("\n------------\n")
# 指定索引
print(df.loc["day2"])

运行结果为

1
2
3
4
5
6
7
8
      calories  duration
day1 420 50
day2 380 40
day3 390 45
------------
calories 380
duration 40
Name: day2, dtype: int64

1.4 CSV文件数据

CSV(Comma-Separated Values,逗号分隔值,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本)

1.4.1 基本使用

1
2
3
4
5
import pandas as pd

df = pd.read_csv('demo.csv')

print(df.to_string())

to_string() 用于返回 DataFrame 类型的数据,如果不使用该函数,则输出结果为数据的前面 5 行和末尾 5 行,直接部分以 代替

对于以下代码

1
2
3
4
5
import pandas as pd

df = pd.read_csv('demo.csv')
print("\n")
print(df)

输出结果如下

1
2
3
4
5
6
7
8
9
10
11
12
     5.1  3.5  1.4  0.2     Iris-setosa
0 4.9 3.0 1.4 0.2 Iris-setosa
1 4.7 3.2 1.3 0.2 Iris-setosa
2 4.6 3.1 1.5 0.2 Iris-setosa
3 5.0 3.6 1.4 0.2 Iris-setosa
4 5.4 3.9 1.7 0.4 Iris-setosa
.. ... ... ... ... ...
144 6.7 3.0 5.2 2.3 Iris-virginica
145 6.3 2.5 5.0 1.9 Iris-virginica
146 6.5 3.0 5.2 2.0 Iris-virginica
147 6.2 3.4 5.4 2.3 Iris-virginica
148 5.9 3.0 5.1 1.8 Iris-virginica

注意CSV文件中的第一行这里视为标题了。

若想忽略标题,可以使用以下方式读取

1
data = pd.read_csv(path, header=None)

1.4.2 将数据保存为CSV文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import pandas as pd

# 三个字段 name, site, age
nme = ["Google", "Runoob", "Taobao", "Wiki"]
st = ["www.google.com", "www.runoob.com", "www.taobao.com", "www.wikipedia.org"]
ag = [90, 40, 80, 98]

# 字典
dict = {'name': nme, 'site': st, 'age': ag}

df = pd.DataFrame(dict)

# 保存 dataframe
df.to_csv('site.csv')

运行上述代码,会得到一个csv文件,文件内容如下

1
2
3
4
5
,name,site,age
0,Google,www.google.com,90
1,Runoob,www.runoob.com,40
2,Taobao,www.taobao.com,80
3,Wiki,www.wikipedia.org,98

1.4.3 数据处理

head( *n* ) 方法用于读取前面的 n 行,如果不填参数 n ,默认返回 5 行

1
2
3
4
5
import pandas as pd

df = pd.read_csv('demo.csv')

print(df.head())

运行结果如下

1
2
3
4
5
6
   5.1  3.5  1.4  0.2  Iris-setosa
0 4.9 3.0 1.4 0.2 Iris-setosa
1 4.7 3.2 1.3 0.2 Iris-setosa
2 4.6 3.1 1.5 0.2 Iris-setosa
3 5.0 3.6 1.4 0.2 Iris-setosa
4 5.4 3.9 1.7 0.4 Iris-setosa

tail()

tail( *n* ) 方法用于读取尾部的 n 行,如果不填参数 n ,默认返回 5 行,空行各个字段的值返回 NaN

1
2
3
4
5
import pandas as pd

df = pd.read_csv('demo.csv')

print(df.tail(3))

运行代码,结果为

1
2
3
4
     5.1  3.5  1.4  0.2     Iris-setosa
146 6.5 3.0 5.2 2.0 Iris-virginica
147 6.2 3.4 5.4 2.3 Iris-virginica
148 5.9 3.0 5.1 1.8 Iris-virginica

info()

info() 方法返回表格的一些基本信息:

1
2
3
4
5
import pandas as pd

df = pd.read_csv('demo.csv')

print(df.info())

结果如下

1
2
3
4
5
6
7
8
9
10
11
12
13
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 149 entries, 0 to 148 # 行数,148 行,第一行编号为 0
Data columns (total 5 columns): # 列数,5列
# Column Non-Null Count Dtype # 各列的数据类型
--- ------ -------------- -----
0 5.1 149 non-null float64 # non-null,意思为非空的数据
1 3.5 149 non-null float64
2 1.4 149 non-null float64
3 0.2 149 non-null float64
4 Iris-setosa 149 non-null object
dtypes: float64(4), object(1) # 类型
memory usage: 5.9+ KB
None

1.5 JSON数据

1.5.1 基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import pandas as pd

data = [
{
"id": "A001",
"name": "菜鸟教程",
"url": "www.runoob.com",
"likes": 61
},
{
"id": "A002",
"name": "Google",
"url": "www.google.com",
"likes": 124
},
{
"id": "A003",
"name": "淘宝",
"url": "www.taobao.com",
"likes": 45
}
]
df = pd.DataFrame(data)

print(df)

运行结果如下

1
2
3
4
     id    name             url  likes
0 A001 菜鸟教程 www.runoob.com 61
1 A002 Google www.google.com 124
2 A003 淘宝 www.taobao.com 45

1.5.2 从 URL 中读取 JSON 数据

1
2
3
4
5
import pandas as pd

URL = 'https://static.runoob.com/download/sites.json'
df = pd.read_json(URL)
print(df)

运行结果如下

1
2
3
4
     id    name             url  likes
0 A001 菜鸟教程 www.runoob.com 61
1 A002 Google www.google.com 124
2 A003 淘宝 www.taobao.com 45

二 matplotlib.pyplot

Matplotlib 是 Python 的绘图库。 它可与 NumPy 一起使用,提供了一种有效的 MatLab 开源替代方案。 它也可以和图形工具包一起使用,如 PyQt 和 wxPython

pip3 安装:

1
pip3 install matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple

使用pyplot

1
import matplotlib.pyplot as plt

也可以使用下面的方法使用

1
from matplotlib import pyplot as plt

2.1 基础使用

2.1.1 简单使用

第一个简单的应用

1
2
3
4
5
6
7
8
9
10
import numpy as np
from matplotlib import pyplot as plt

x = np.arange(1, 11)
y = 2 * x + 5
plt.title("Matplotlib demo")
plt.xlabel("x axis caption")
plt.ylabel("y axis caption")
plt.plot(x, y)
plt.show()

运行之后,可以得到下面的一副图片

image-20210525155002285

2.1.2 中文支持

对于2.1.1中的实例,在默认情况下是不支持中文的,如设置了中文,则会出现乱码,因此需要通过设置以支持汉字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import numpy as np
from matplotlib import pyplot as plt
import matplotlib as mpl

x = np.arange(1, 11)
y = 2 * x + 5

# 支持中文
mpl.rcParams["font.sans-serif"] = ["simHei"]
mpl.rcParams["axes.unicode_minus"] = False

plt.title("标题")
plt.xlabel("X轴")
plt.ylabel("Y轴")
plt.plot(x, y)
plt.show()

运行代码,得到的结果如下

image-20210525155528322

2.2 图形绘制

2.2.1 简单图形

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import matplotlib.pyplot as plt
import matplotlib as mpl

# 图形输入值
input_values = [1, 2, 3, 4, 5]
# 图形输出值
squares = [1, 4, 9, 16, 25]

# 支持中文
mpl.rcParams["font.sans-serif"] = ["simHei"]
mpl.rcParams["axes.unicode_minus"] = False

# plot根据列表绘制出有意义的图形,linewidth是图形线宽,可省略
plt.plot(input_values, squares, linewidth=5)
# 设置图标标题
plt.title("简单图形绘制", fontsize=30)
# 设置坐标轴标签
plt.xlabel("X轴", fontsize=8)
plt.ylabel("Y轴 ", fontsize=14)
# 设置刻度标记的大小
plt.tick_params(axis='both', labelsize=14)
# 打开matplotlib查看器,并显示绘制图形
plt.show()

结果如下

image-20210525160053974

2.2.2 绘制点

1
2
3
4
5
6
7
8
import matplotlib.pyplot as plt
import matplotlib as mpl

# 绘制散点图(传如一对x和y坐标,在指定位置绘制一个点)
plt.scatter(2, 4)
# 设置输出样式
plt.scatter(3, 5, s=200)
plt.show()

结果为

image-20210525160219850

2.2.3 一系列的点

1
2
3
4
5
6
7
8
9
import matplotlib.pyplot as plt
import matplotlib as mpl

x_values = [1, 2, 3, 4, 5]
y_values = [1, 5, 10, 15, 20]

plt.scatter(x_values, y_values, s=100)

plt.show()

结果为

image-20210525160500409

2.2.4 自动计算数据

1
2
3
4
5
6
7
8
9
10
11
import matplotlib.pyplot as plt
import matplotlib as mpl

x_values = list(range(1, 1001))
y_values = [x ** 2 for x in x_values]

plt.scatter(x_values, y_values, s=100)

# 设置每个坐标轴的取值范围(x轴取值,y轴取值)
plt.axis([0, 1100, 0, 1100000])
plt.show()

image-20210525160636766

2.2.5 删除数据点的轮廓

1
2
3
4
5
6
7
8
9
10
11
12
13
import matplotlib.pyplot as plt
import matplotlib as mpl

x_values = list(range(1, 1001))
y_values = [x ** 2 for x in x_values]

# matplotlib允许你给散点图中的各个点指定颜色。默认为蓝色点和黑色轮廓,在散点图包含的 数据点不多时效果很好。但绘制很多点时,黑色轮廓可能会粘连在一起。
# edgecolor='none'删除数据点的轮廓
plt.scatter(x_values, y_values, edgecolor='none', s=40)

# 设置每个坐标轴的取值范围
plt.axis([0, 1100, 0, 1100000])
plt.show()

image-20210525160807120

2.2.6 自定义颜色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import matplotlib.pyplot as plt
import matplotlib as mpl

x_values = list(range(1, 1001))
y_values = [x ** 2 for x in x_values]

# 自定义颜色c=''直接传颜色或元组都可以
# matplotlib允许你给散点图中的各个点指定颜色。默认为蓝色点和黑色轮廓,在散点图包含的 数据点不多时效果很好。但绘制很多点时,黑色轮廓可能会粘连在一起。
# edgecolor='none'删除数据点的轮廓
plt.scatter(x_values, y_values, c='green', edgecolor='none', s=40)
# plt.scatter(x_values, y_values, c=(0, 0, 0.8), edgecolor='none', s=40)


# 设置每个坐标轴的取值范围
plt.axis([0, 1100, 0, 1100000])
plt.show()

image-20210525161250401

2.2.7 使用颜色映射

将c设置成一个y值列表,并使用参数cmap告诉pyplot使用那个颜色映射,这些代码将y值较小的点显示为浅蓝色,将y值较大的点显示为深蓝色

1
2
3
4
5
6
7
8
9
10
11
12
13
import matplotlib.pyplot as plt
import matplotlib as mpl

x_values = list(range(1, 1001))
y_values = [x ** 2 for x in x_values]

# matplotlib允许你给散点图中的各个点指定颜色。默认为蓝色点和黑色轮廓,在散点图包含的 数据点不多时效果很好。但绘制很多点时,黑色轮廓可能会粘连在一起。
# edgecolor='none'删除数据点的轮廓
plt.scatter(x_values, y_values, c=y_values, cmap=plt.cm.Blues, edgecolor='none', s=40)

# 设置每个坐标轴的取值范围
plt.axis([0, 1100, 0, 1100000])
plt.show()

image-20210525161422560

2.2.8 自动保存图表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import matplotlib.pyplot as plt
import matplotlib as mpl

x_values = list(range(1, 1001))
y_values = [x ** 2 for x in x_values]

# matplotlib允许你给散点图中的各个点指定颜色。默认为蓝色点和黑色轮廓,在散点图包含的 数据点不多时效果很好。但绘制很多点时,黑色轮廓可能会粘连在一起。
# edgecolor='none'删除数据点的轮廓
plt.scatter(x_values, y_values, c=y_values, cmap=plt.cm.Blues, edgecolor='none', s=40)

# 设置每个坐标轴的取值范围
plt.axis([0, 1100, 0, 1100000])
# plt.show()
# 参数1指定要以什么样的文件名保存图表,保存和代码的同目录下,第二个参数表示要将多余的空白区域剪掉,要保留空白区域,可省略第二个参数
plt.savefig('demo.png', bbox_inches='tight')

2.2.9 绘制条形图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import matplotlib.pyplot as plt
import matplotlib as mpl

# 支持中文
mpl.rcParams["font.sans-serif"] = ["simHei"]
mpl.rcParams["axes.unicode_minus"] = False

x = [5, 8, 10]
y = [12, 16, 6]
x2 = [6, 9, 11]
y2 = [6, 15, 7]
plt.bar(x, y, align='center')
plt.bar(x2, y2, color='g', align='center')
plt.title('条形图')
plt.ylabel('Y轴')
plt.xlabel('X轴')
plt.show()

image-20210525161907078

2.2.10 绘制直方图

Matplotlib 可以将直方图的数字表示转换为图形。 pyplot 子模块的 plt() 函数将包含数据和 bin 数组的数组作为参数,并转换为直方图

1
2
3
4
5
6
7
8
9
10
11
12
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np

# 支持中文
mpl.rcParams["font.sans-serif"] = ["simHei"]
mpl.rcParams["axes.unicode_minus"] = False

a = np.array([22, 87, 5, 43, 56, 73, 55, 54, 11, 20, 51, 5, 79, 31, 27])
plt.hist(a, bins=[0, 20, 40, 60, 80, 100])
plt.title("直方图")
plt.show()

image-20210525162105000