1.什么是数据分析
数据分析是指用适当的方法对收集来的大量方法进行分析,帮助人们做出判断,采取适当行动。
数据分析的流程是:
1.提出问题
2.准备数据
3.分析数据
4.获得结论
5.成功可视化
2.conda环境安装
可参考以下视频https://b23.tv/EIZpKXM,里面详细介绍了anaconda的下载方法,如何创建虚拟环境以及如何在pycharm中将解释器设置为conda的解释器,这样使得pycharm能够使用不同的解释器,且解释器之间相互独立互补干扰。
3.matplotlib
coplitlib是目前python最流行的数据可视化库之一,主要用于数据的处理
3.1绘制折线图
下面是一个简单的示例
from matplotlib import pyplot as plt
import random
import matplotlib
config = {
"font.family":['Microsoft Yahei', 'SimSun'],
}
plt.rcParams.update(config)
plt.figure(figsize=(10,10), dpi=300)
x = range(11, 31)
y = [1, 0, 1, 1, 2, 4, 3, 2, 3, 4, 4, 5, 6, 5, 3, 3, 1, 1, 1, 4]
_x_label = [f"{i}岁" for i in x]
plt.xticks(x, _x_label, rotation=90)
plt.plot(x, y)
plt.show()
plt.savefig('graph.png')
方法figure用来调整图片的大小和清晰度。
需要注意的是,方法figure要放在所有绘图操作之前,这样才能让后续操作在其绘制的图上进行,像xticks等方法会进行隐式绘图,如果将方法figure放在其后,会出现多余的图片。
首先规定x轴和y轴,xticks方法用来修改x轴上的刻度,用yticks方法修改y轴上的刻度。可在刻度对应位置显示指定字符,这是在xticks方法中传入第二个参数,要注意与前面的刻度一一对应。由于matplotlib默认不支持显示中文,可以像代码中加入一个全局变量来添加能显示中文的字体,也可以在需要加标签的地方添加指定字体,如:
plt.xticks(x, _x_label, rotation=90, fontfamily="SimSun")
在 Matplotlib 中,字体回退机制(Font Fallback) 是指当指定的字体无法显示某些字符(如中文、特殊符号等)时,系统会自动尝试使用其他备用字体来渲染这些字符,以避免显示为方块或乱码。这一机制主要通过 字体属性配置 和 Matplotlib 的字体管理系统 实现。
字体回退的优先级规则
-
用户指定字体 > 全局默认字体
若在代码中显式设置font.family
,会覆盖 Matplotlib 的默认字体(通常为无衬线字体如 DejaVu Sans)。 -
系统字体 > Matplotlib 内置字体
Matplotlib 会优先使用系统已安装的字体(如 Windows 的 SimHei、macOS 的 Heiti TC),若未找到则尝试内置字体(如 Noto Sans CJK)。 -
按列表顺序尝试备用字体
备用字体按font.family
列表的顺序依次生效,直到找到支持字符的字体。
plt.grid(True)
可通过grid方法来绘制网格,网格的疏密通过改变x轴和y轴的刻度来实现。添加参数alpha来改变网格线条的透明度。
plt.xlabel('岁数')
plt.ylabel('谈的女朋友数量')
plt.title('graph')
上面三个方法分别为x轴,y轴,整个表格设置标签。
plot方法用于绘制图形,接收可迭代且元素可被解释为数值或者与绘图相关的数据类型为坐标参数,plot可传入多种参数来设置线段风格,如参数color来设置颜色,参数linestyle来设置线条风格,如linestyle='--', linewith来设置线条粗细, alpha来设置透明度。
如果想在同一张图上绘制更多曲线,只需要继续plot几次就行。
plot方法中还可以加入参数label来为曲线加入标签,不过添加标签后还要调用方法legend来添加图例。
plt.plot(x, y, label='自己')
plt.legend() #添加图例
接收字体的参数有prop,fontproperties和fontfamily,它们的区别如下:
功能与灵活性
• fontproperties 和 prop:功能基本相同,都非常灵活,可通过 FontProperties 类来精确设置字体的多种属性,如字体文件、大小、粗细、倾斜等。
• fontfamily:功能相对单一,主要用于指定字体家族,只能简单地选择预定义的字体类别或具体字体名称,无法直接设置字体的其他详细属性。
使用方式
• fontproperties 和 prop:使用时都需要先创建 FontProperties 对象,然后分别作为 fontproperties 参数或 prop 参数传递给相关函数。
• fontfamily:直接将字体家族名称作为参数值传入相关函数,使用起来更加简洁。
适用场景
• fontproperties 和 prop:适用于需要对字体进行精细控制,使用特殊字体文件或需要同时设置多个字体属性的场景。
• fontfamily:适用于只需快速指定常见字体家族,对字体其他属性要求不高,追求简洁设置的场景。
在实际使用中,如果只是想简单地指定字体家族,使用 fontfamily 即可;如果需要更全面、精确地控制字体属性,则使用 fontproperties 或 prop,具体选择哪个参数可根据个人习惯和代码风格来决定。
注意,legend方法只能接收prop作为字体参数。
通过ctrl+b可查看源码来查看方法的用法,可以通过loc参数来调整图例的位置。
plt.savefig()用来保存图片。
总结:前面主要学习了:
1.绘制折线图
2.设置图片大小和分辨率
3.实现了图片的保存
4.设置xy轴的刻度和字符串
5.解决了刻度稀疏和稠密问题
6.设置了标题
7.设置了字体
8.在一个图上绘制了多个图形
9.为不同图形添加图例
3.2 绘制散点图
代码示例如下:
from matplotlib import pyplot as plt
import random
config = {
"font.family": ['Microsoft Yahei', 'SimSun'],
}
plt.rcParams.update(config)
y_3 = []
y_10 = []
for i in range(20):
y_3.append(random.randint(0, 20))
for i in range(20):
y_10.append(random.randint(10, 30))
x = range(20)
x_labels = [f"{i}时" for i in list(x)[::2]]
plt.xticks(list(x)[::2], x_labels)
plt.scatter(x, y_3, color='blue')
plt.scatter(x, y_10, color='red')
plt.show()
这里只是将方法plot换成了方法scatter就成功绘制了散点图。
3.3绘制条形图
代码如下:
from matplotlib import pyplot as plt
import random
config = {
"font.family": ['Microsoft Yahei', 'SimSun'],
}
plt.rcParams.update(config)
a = ['阿凡达', '泰坦尼克号', '星球大战', '复仇者联盟', '蜘蛛侠']
b = [212.00, 164.23, 150.19, 202.99, 141.60]
plt.xticks(range(len(a)), a)
plt.bar(range(len(a)), b, width=0.2)
plt.show()
bar中第一个数值类型需要是数值类型来对应位置,不过在 Matplotlib 中,当你直接使用字符串列表(如a = ['阿凡达', '泰坦尼克号', ...]
)作为plt.bar()
的 x 参数时,Matplotlib 会自动处理非数值类型的标签,Matplotlib 会隐式地将字符串标签映射为连续的整数位置(0, 1, 2, ...),但在显示时,Matplotlib 会保留原始的字符串标签,而不是显示数字。上面的写法更加严谨。
使用方法barh来绘制横着的条形图。
from matplotlib import pyplot as plt
import random
config = {
"font.family": ['Microsoft Yahei', 'SimSun'],
}
plt.rcParams.update(config)
a = ['阿凡达', '泰坦尼克号', '星球大战', '复仇者联盟', '蜘蛛侠']
b = [212.00, 164.23, 150.19, 202.99, 141.60]
plt.yticks(range(len(a)), a, rotation=0)
plt.barh(range(len(a)), b, height=0.2)
plt.show()
注意将width改为height,xticks改为yticks。
3.4绘制直方图
代码如下:
from matplotlib import pyplot as plt
import random
config = {
"font.family": ['Microsoft Yahei', 'SimSun'],
}
plt.rcParams.update(config)
a = [95, 102, 117, 125, 108, 99, 112, 103, 121, 115,
107, 128, 98, 119, 101, 123, 110, 97, 120, 106,
114, 127, 93, 109, 118, 122, 96, 104, 113, 124,
105, 116, 94, 126, 100, 111, 92, 129, 110, 107,
121, 98, 115, 103, 128, 99, 119, 102, 125, 117,
108, 97, 123, 112, 106, 95, 120, 114, 101, 127,
93, 118, 109, 122, 96, 113, 104, 124, 116, 105,
94, 129, 111, 100, 92, 126, 110, 107, 121, 98,
115, 103, 128, 99, 119, 102, 125, 117, 108, 97,
123, 112, 106, 95, 120, 114, 101, 127, 93, 118]
bin_width = 3
num_bins = int((max(a)-min(a))/bin_width)
plt.hist(a, bins=num_bins)
plt.show()
绘制直方图使用方法hist,主要需要传入数据以及需要分成的组数,这里取a中最大值减去最小值再除以宽度作为组数。bins的值还可以是数组,用来表示组局不同的情况。
如果要将频数分布直方图改为频率分布直方图,著需要在hist中加入参数:
plt.hist(a, bins=num_bins, density=True)
注意,使用hist方法来绘图时只能用原始数据,如果像每一组有多少以及统计好了,这时候需要用绘制条形图的方法而不能使用方法hist。
上面就是matplotlib常用的方法,如果有其他绘图需求,可以访问:http://matplotlib.org/gallery/index.htmlhttp://matplotlib.org/gallery/index.html
4. numpy
numpy是一个在python中做科学计算的基础库,重在数值计算,也是大部分python科学计算库的基础库,多用在大型,多维数组上执行数值运算。
4.1numpy创建数组
import numpy as np
t1 = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
t2 = np.array(range(1, 11))
t3 = np.arange(4, 10, 2) #生成一个等差数列
print(t1, t2, t3)
print(type(t1), type(t2), type(t3))
print(t3.dtype)
可使用以上方法创建ndarry类型的数组,方法需要传入一个序列对象,像列表、元组等,然后基于这个序列来创建数组,返回的是一个 NumPy 数组,数组内元素的类型由输入数据决定。方法arange会根据指定的起始值、结束值以及步长来生成一个等差数列,返回的同样是 NumPy 数组,但数组元素的类型默认是整数(比如 int64
),当然也可以通过参数来指定其他类型。
dtype则是数组中所存放的数据的数据类型。numpy中的数据类型如下:
t4 = np.array(range(1, 4), dtype='float64')
可以直接指定数据类型。
使用方法astype来改变数据类型:
t6 = t5.astype("int8")
方法round用来指定小数保留的位数。
t5 = np.array([random.random() for i in range(5)])
t6 = np.round(t5, 2)
print(t6)
可使用如下方法来创建多维数组:
import numpy as np
import random
t1 = np.array([1,2,3,4,5,6,7,8,9,10])
print(t1.shape) #创建一维数组
t2 = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(t2.shape) #创建二维数组
t3 = np.array([[[1, 2], [3, 4], [5, 6]],[[7, 8], [9, 10], [11, 12]]])
print(t3) #创建三维数组
注意,三维数组的每一层数组大小应该相等。
shape是 NumPy 数组的一个基本属性,它返回一个元组(tuple),表示数组在每个维度上的大小。
方法reshape用来修改数组的行数和列数:
import numpy as np
t1 = np.array([1,2,3,4,5,6,7,8,9,10])
t2 = t1.reshape(2, 5)
print(t2)
下面的代码中shape[0]和shape[1]分别表示二维数组的行数和列数,而在三维数组中shape[0],shape[1],shape[2]分别代表数组深度,每层数组的行数和列数。
import numpy as np
t1 = np.array([1,2,3,4,5,6,7,8,9,10])
t2 = t1.reshape(2, 5)
print(t2.shape[0], t2.shape[1])
import numpy as np
t3 = np.array([[[1, 2], [4, 5]],
[[7, 8], [10, 11]],
[[13, 14], [16, 17]],])
print(t3.shape[0], t3.shape[1], t3.shape[2])
方法flatten将矩阵转化为一维数组:
t1 = np.array([1,2,3,4,5,6,7,8,9,10])
t2 = t1.reshape(2, 5)
t3 = t2.flatten()
print(t3)
使用如下方法创建全零数组,全1数组和全空数组:
data = np.zeros(shape=(5, 3))
data = np.ones(shape=(5, 3))
data = np.empey(shape=(5,3))
使用方法linspace来创建有连续间隔的数组,也可以称为线性等分向量(linear space),在一个指定的区间内按照指定的步长,将区间均等分,生成一个线段类型的数组。
data = np.linspace(1,10,20)
有两种方法可以创建一个具有特定形状的数组:
import numpy as np
data_1 = np.random.rand(3, 4)
data_2 = np.random.randint(2,5,size=(4,5)) #创建一个四行五列的随机数组,每个元素的值在2到5之间
4.2numpy数组的属性
1.ndarray.shape
返回一个包含数组维度的元组:
import numpy as np
a = np.array([1, 2, 3],[4, 5, 6])
print(a.shape)
2.ndarray.shape
此属性会返回一个元组,元组中的元素为数组每个维度的大小。对于有 n 行 m 列的矩阵,其 shape 为 (n, m)。
a = np.array([[1, 2, 3], [4, 5, 6]])
print(a.shape) # 输出(2, 3),意味着数组有2行3列
3.ndarray.size
该属性返回的是数组中所有元素的总数,其值等于 shape 中各元素的乘积。
a = np.array([[1, 2, 3], [4, 5, 6]])
print(a.size) # 输出6,因为数组共有6个元素
4.ndarray.dtype
这个属性用于描述数组中元素的数据类型,像 'int32'、'float64' 等都有可能。
a = np.array([1, 2, 3])
print(a.dtype) # 输出dtype('int64')
b = np.array([1.0, 2.0, 3.0])
print(b.dtype) # 输出dtype('float64')
5.ndarray.itemsize
它表示的是数组中每个元素所占的字节数,例如,类型为 'float64' 的元素,其 itemsize 为 8(8 字节等于 64 位)。
a = np.array([1, 2, 3], dtype=np.float64)
print(a.itemsize) # 输出8,说明每个元素占8字节
6.nbarray.data
该属性是一个缓冲区,里面包含了数组的实际元素。不过,在实际使用中,我们通常会通过索引来访问元素,而不是直接使用这个缓冲区。
a = np.array([1, 2, 3])
print(a.data) # 输出<memory at 0x7f...>
7.ndarray.nbytes
此属性返回的是数组中所有元素占用的总字节数,它等于 size 乘以 itemsize。
a = np.array([1, 2, 3], dtype=np.float64)
print(a.nbytes) # 输出24,因为3个元素,每个占8字节
8.ndarray.T
该属性用于返回数组的转置,如果数组是一维的,转置后保持不变;如果是二维的,转置就是行和列互换。
a = np.array([[1, 2], [3, 4]])
print(a.T) # 输出[[1 3], [2 4]]
4.3数组的计算
在 NumPy 中,数组计算遵循一套高效且灵活的法则,主要包括逐元素运算、广播机制和轴(axis)操作。
NumPy 数组的基本运算默认是逐元素进行的,要求数组形状相同。
import numpy as np
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
# 加法、减法、乘法、除法
print(a + b) # 输出: [5 7 9]
print(a - b) # 输出: [-3 -3 -3]
print(a * b) # 输出: [4 10 18]
print(a / b) # 输出: [0.25 0.4 0.5]
# 比较运算(返回布尔数组)
print(a > b) # 输出: [False False False]
当数组形状不同时,NumPy 会通过广播自动扩展数组,使它们可以兼容运算。
广播机制如下:
1.从右向左比较维度:比较两个数组的形状,从最后一个维度开始向左匹配
2.维度相等或其中一个为 1:维度相等则直接运算,若其中一个为1则扩展该维度来匹配另一个数组。
3.维度不足则补1:若一个数组维度较少,则在左侧补 1 直到维度数匹配。
示例如下:
# 示例1:标量与数组广播
a = np.array([1, 2, 3])
b = 2
print(a * b) # 输出: [2 4 6] → b被广播为[2, 2, 2]
# 示例2:二维数组与一维数组广播
a = np.array([[1, 2, 3], [4, 5, 6]]) # 形状: (2, 3)
b = np.array([10, 20, 30]) # 形状: (3,) → 补1为(1, 3)
print(a + b) # 输出:
# [[11 22 33]
# [14 25 36]]
# b被广播为: [[10, 20, 30], [10, 20, 30]]
NumPy 中的许多函数(如 sum
、mean
、max
)可以沿着指定轴进行计算。轴即如二维数组的行,列,三维数组的深度等,示例如下:
a = np.array([[1, 2], [3, 4], [5, 6]]) # 形状: (3, 2)
# 沿axis=0(列方向)求和
print(np.sum(a, axis=0)) # 输出: [9 12] → 每列的和
# 沿axis=1(行方向)求和
print(np.sum(a, axis=1)) # 输出: [3 7 11] → 每行的和
# 三维数组示例
b = np.array([
[[1, 2], [3, 4]],
[[5, 6], [7, 8]]
]) # 形状: (2, 2, 2)
# 沿axis=0(深度方向)求和
print(np.sum(b, axis=0)) # 输出:
# [[6 8]
# [10 12]]
可使用以下方法来实现矩阵运算:
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
# 使用 np.dot() 或 @ 运算符
print(np.dot(a, b)) # 输出:
# [[19 22]
# [43 50]]
print(a @ b) # 等价于 np.dot()
a = np.array([[1, 2], [3, 4]])
# 转置
print(a.T) # 输出:
# [[1 3]
# [2 4]]
# 逆矩阵
print(np.linalg.inv(a)) # 输出:
# [[-2. 1. ]
# [ 1.5 -0.5]]
4.4数组的索引和切片
ndarray
对象的内容可以通过索引或切片来访问和修改,就像 Python 的内置容器对象一样。
ndarray
对象中的元素遵循基于零的索引。 有三种可用的索引方法类型: 字段访问,基本切片和高级索引。
import numpy as np
t3 = np.array([[[1, 2], [4, 5]],
[[7, 8], [10, 11]],
[[13, 14], [16, 17]],])
print(t3[1,0,1])
即方括号内分别指定要访问的元素在各个维度上的位置。
对于一维数组的切片,指定起始位置和步长来获得切片:
import numpy as np
array = np.array([1, 2, 3, 4, 5, 6, 7])
print(array[1:6:2])
对于多维数组的切片,简单来说就是一维一维的分解,多维数组的切片格式为:
arr[start:stop:step, start:stop:step, ...]
如对于一个二维数组:
import numpy as np
arr = np.array([
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
])
# 提取第2行(索引1)
row = arr[1, :] # 输出: [5, 6, 7, 8]
# 提取第3列(索引2)
col = arr[:, 2] # 输出: [3, 7, 11]
# 提取左上角2x2子矩阵
sub = arr[:2, :2] # 输出: [[1, 2], [5, 6]]
# 提取右下角2x2子矩阵
sub = arr[1:, 2:] # 输出: [[7, 8], [11, 12]]
# 每隔一行/列提取元素
strided = arr[::2, ::2] # 输出: [[1, 3], [9, 11]]
对于一个三维数组:
arr_3d = np.array([
[[1, 2], [3, 4]], # 第一个2x2矩阵
[[5, 6], [7, 8]] # 第二个2x2矩阵
])
# 提取第一个矩阵
mat1 = arr_3d[0] # 等价于 arr_3d[0, :, :],输出: [[1, 2], [3, 4]]
# 提取所有矩阵的第一行
first_rows = arr_3d[:, 0, :] # 输出: [[1, 2], [5, 6]]
# 提取所有矩阵的左上角元素
corners = arr_3d[:, 0, 0] # 输出: [1, 5]
负索引同样适用。
4.5数组中数据统计常用方法
1. numpy.mean(arr, axis=None, dtype=None, out=None):
计算数组的平均值。参数axis表示沿着哪个轴进行计算,默认为None,表示计算整个数组的平均值;dtype表示返回结果的数据类型,默认为float64;out表示将结果存储在指定的数组中,一般情况下,传个数组进去就可以了,其他的用默认的就好。
2.numpy.median(arr, axis=None, out=None):
计算数组的中位数。参数axis和out的含义与numpy.mean()相同
3.numpy.std(arr, axis=None, dtype=None, out=None):
计算数组的标准差。参数axis、dtype和out的含义与numpy.mean()相同。
4.numpy.var(arr, axis=None, dtype=None, out=None):
计算数组的方差。参数axis、dtype和out的含义与numpy.mean()相同。
5.numpy.min(arr, axis=None, out=None):
计算数组的最小值。参数axis和out的含义与numpy.mean()相同。
6.numpy.max(arr, axis=None, out=None):
计算数组的最大值。参数axis和out的含义与numpy.mean()相同
7.numpy.sum(arr, axis=None, dtype=None, out=None):
计算数组的元素之和。参数axis、dtype和out的含义与numpy.mean()相同。
8.numpy.prod(arr, axis=None, dtype=None, out=None):
计算数组的元素乘积。参数axis、dtype和out的含义与numpy.mean()相同
9.numpy.cumsum(arr, axis=None, dtype=None, out=None):
计算数组的累积和。参数axis、dtype和out的含义与numpy.mean()相同。
4.6数组的堆叠
垂直堆叠:
# 垂直堆叠
stacked_vertically = np.vstack((array1, array2))
水平堆叠:
# 水平堆叠
stacked_horizontally = np.hstack((array1, array2))
4.7数组的保存与加载
数组的保存:
# 保存数组到文件
np.save('my_array.npy', data)
数组的加载:
# 加载数组
loaded_data = np.load('my_array.npy')