数据分析三剑客:NumPy、Pandas、Matplotlib
数据分析三剑客:
一、能说出NumPy、Pandas、Matplotlib的关系:
NumPy是底层数值计算基础:
NumPy提供了高效的多维数组和数值计算能力,是后两者的基础,比如Pandas里的DataFrame底层存储的其实就是NumPy数组。Pandas 的数值运算(像求均值、求和)最终都是调用 NumPy 的函数来实现的。
Pandas 是基于它做结构化数据分析的:
Pandas是中层工具,它在NumPy的基础上,把数组包装成了更适合业务分析的表格结构,(Series 是单列数组,DataFrame 是多列表格),还加了索引、列名这些方便理解的信息,更适合处理结构化数据。
Matplotlib 靠前两者提供的数据做可视化。
Matplotlib就是上层展示,它可以直接接收?NumPy数组或者Pandas的Series/DataFrame 作为数据源,把数据转换成图表(比如折线图、散点图),相当于把前两者处理好的数据可视化出来,方便直观理解。
简单说就是:NumPy 负责 “算得快”,Pandas 负责 “用得顺”,Matplotlib 负责 “看得懂”,三者是从底层计算到中层处理再到上层展示的递进关系。
二、掌握Numpy数组创建操作:np.array() 或者 np.random.randn()
-
这块我用得很多:
-
用
np.array()的话,既可以把Python的列表转成数组(比如np.array([1,2,3])生成一维数组,np.array([[1,2],[3,4]])生成二维数组),也能指定数据类型(比如加dtype=np.float32把数据存成浮点型,节省内存)。 -
用
np.random.randn()是生成服从标准正态分布的随机数组,比如np.random.randn(3,4)就能生成3行4列、均值0、标准差1的随机浮点数;如果是生成整数随机数组,我也常用np.random.randint(0,10,size=(2,3))(生成0到10之间的整数,2行3列)。
另外像生成全0数组(np.zeros())、全1数组(np.ones())这些常用的创建方式,我也都很熟练。
# 导包
import numpy as np
# TODO 演示创建numpy数组的多种方式
# 方式1: 列表转换numpy
n1 = np.array([1, 2, 3])
print(type(n1), n1, n1.ndim, n1.shape)
n1 = np.array([[1, 2, 3]])
print(type(n1), n1, n1.ndim, n1.shape)
# 方式2: 随机生成0-1范围数据或者正态分布数据集
n3 = np.random.rand(3, 2)
print(type(n3), n3.ndim, n3.shape)
print(n3)
n3 = np.random.randn(3, 2)
print(type(n3), n3.ndim, n3.shape)
print(n3)
# 方式3: 全0全1的numpy
n2 = np.zeros(shape=(3, 2))
print(type(n2), n2.ndim, n2.shape)
print(n2)
n2 = np.ones(shape=(3, 2))
print(type(n2), n2.ndim, n2.shape)
print(n2)
# 方式4: 生成序列
n4 = np.arange(1, 10)
print(type(n4), n4.ndim, n4.shape)
print(n4)
n4 = np.linspace(1, 10, num=11)
print(type(n4), n4.ndim, n4.shape)
print(n4)
三、了解numpy数组的相关操作:属性、索引、形状、运算
-
属性:
-
比如看数组的形状用
arr.shape,看数据类型用arr.dtype,看数组元素个数用arr.size,这些属性能帮我快速了解数组的基本信息。arr.ndim是数组的维度。# 创建示例数组 arr = np.array([[1, 2, 3], [4, 5, 6]]) print("数组:\n", arr) print("数组维度:", arr.ndim) # 维度数 print("数组形状:", arr.shape) # 形状 (行数, 列数) print("数组大小:", arr.size) # 元素总数 print("数据类型:", arr.dtype) # 数据类型 -
索引:
-
一维数组直接用
arr[0]取第一个元素;二维数组可以用arr[1,2]取第2行第3列,
也能切片(比如arr[0:2, 1:3]取前2行、第2到3列的区域);
如果是条件索引,比如arr[arr>5]可以直接取出数组中大于5的元素。import numpy as np # 创建示例数组 arr = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]) print("原始数组:\n", arr) # 索引 print("第一个元素:", arr[0, 0]) # 第一行第一列 print("最后一行:", arr[-1, :]) # 最后一行 print("最后一行:", arr[-1]) # 最后一行 print("第二列:", arr[:, 1]) # 所有行的第二列 print("第二列:", arr[:, 1]) # 所有行的第二列 # 切片 print("前两行:\n", arr[:2,:]) # 前两行 print("前两行:\n", arr[:2]) # 前两行 print("前两行的前两列:\n", arr[:2, :2]) # 前两行前两列 print("每隔一个元素取一个:\n", arr[::2, ::2]) # 行和列都隔一个取一个 # 布尔索引 bool_idx = arr > 5 print(bool_idx) print("大于5的元素:\n", arr[bool_idx]) print("直接布尔索引:\n", arr[arr > 5]) -
形状调整:
-
比如用
arr.reshape(2,6)把原来的数组改成2行6列,arr.T数组的转置。
或者用arr.flatten()把多维数组转成一维,这些在数据格式不匹配的时候经常用。import numpy as np arr = np.arange(12) print("原始一维数组:", arr, arr.ndim, arr.shape) # 重塑形状 arr_2d = arr.reshape(1, 12) print("重塑为1x12数组:\n", arr_2d, arr_2d.ndim, arr_2d.shape) # 重塑形状 arr_2d = arr.reshape(2, 2, 3) print("重塑为2x2x3数组:\n", arr_2d, arr_2d.ndim, arr_2d.shape) # 重塑形状 arr_2d = arr.reshape(3, 4) print("重塑为3x4数组:\n", arr_2d) # 转置 arr_t = arr_2d.T print("转置数组:\n", arr_t) # 展平数组 arr_flat = arr_2d.flatten() print("展平数组:", arr_flat) # 调整大小 arr_resized = np.resize(arr, (4, 5)) print("调整大小:\n", arr_resized) -
运算:
-
数组之间可以直接做加减乘除(比如
arr1 + arr2),也能和标量运算(比如arr * 2);
矩阵乘法:公式:(m,p)*(p,n) = (m,n) API:np.dot
另外像求均值np.mean(arr)、求和np.sum(arr)、求最大值np.max(arr)这些统计运算,我也会根据需求用(比如np.sum(arr, axis=0)是按列求和,axis=1是按行求和)。基础运算
import numpy as np a = np.array([1, 2, 3, 4]) b = np.array([1, 2, 3, 4]) print("数组a:", a) print("数组b:", b) # 算术运算 print("加法:", a + b) print("减法:", a - b) print("乘法:", a * b) print("除法:", a / b) print("幂运算:", a ** 2) # 比较运算 print("大于比较:", a > 2) print("等于比较:", a == b) print('========================================') # TODO 矩阵乘法 matrix_a = np.array([[1, 2], [3, 4]]) matrix_b = np.array([[5, 6], [7, 8]]) print(matrix_a) print(matrix_b) print("矩阵乘法:\n", np.dot(matrix_a, matrix_b)) print('------------------------------------------') matrix_a = np.array([[1, 2, 3], [3, 4, 5]]) matrix_b = np.array([[5, 6, 7], [7, 8, 9]]) # print("矩阵乘法:\n", np.dot(matrix_a, matrix_b)) # 报错 (2,3) (2,3) print("矩阵乘法:\n", np.dot(matrix_a, matrix_b.T)) # (2,3) (3,2) = (2,2) # 注意: 矩阵乘法不满足交换律!!! print(matrix_b.T) print(matrix_a) print("矩阵乘法:\n", np.dot(matrix_b.T,matrix_a)) # (3,2) (2,3) = (3,3)统计运算
import numpy as np arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) print("数组:\n", arr) print("总和:", np.sum(arr)) print("每列总和:", np.sum(arr, axis=0)) # 沿列方向 print("每行总和:", np.sum(arr, axis=1)) # 沿行方向 print("平均值:", np.mean(arr)) print("平均值:", np.mean(arr, axis=0)) print("平均值:", np.mean(arr, axis=1)) print("标准差:", np.std(arr)) print("方差:", np.var(arr)) print("最小值:", np.min(arr)) print("最大值:", np.max(arr)) print("最小值索引:", np.argmin(arr)) print("最大值索引:", np.argmax(arr))
四、能说出Pandas中Series和DataFrame的联系
我很清楚这两者的关系:
首先Series是Pandas里的“单列数据结构”——它像是带索引的一维数组,既有数据值,也有对应的索引(可以是默认的数字索引,也能自己指定比如日期、名称)。
而DataFrame是“多列表格结构”——它可以理解成是由多个Series“拼”起来的,每一列都是一个Series,并且这些Series共享同一个行索引。
举个例子:如果我有一个存“用户年龄”的Series和一个存“用户消费”的Series,把这两个Series按列组合起来,就能得到一个包含“年龄”“消费”两列的DataFrame;反过来,从DataFrame里取某一列(比如df['年龄']),得到的就是对应的Series。
所以两者的核心联系是:DataFrame是Series的容器,Series是DataFrame的组成单元。
五、掌握DataFrame对象的创建操作:从文件读取或者列表/字典等转换
-
这些创建方式我都常用:
-
从文件读取:比如读CSV文件用
pd.read_csv('文件路径.csv'),读Excel文件用pd.read_excel('文件路径.xlsx'),还能指定参数(比如sep='\t'指定分隔符、usecols=[0,2]只读取第1和第3列)。# 方式3: 读取文件 df3 = pd.read_csv('data/测试数据.txt',sep=',',names=['id', 'name', 'price']) print(df3) -
用列表/字典创建:如果是字典,比如
pd.DataFrame({'姓名':['张三','李四'],'年龄':[20,25]}),字典的键会变成列名,值会变成对应列的数据;如果是列表嵌套列表,比如pd.DataFrame([['张三',20],['李四',25]], columns=['姓名','年龄']),需要手动指定列名。
另外像从NumPy数组创建(pd.DataFrame(np.random.rand(3,2), columns=['列1','列2'])),或者从数据库查询结果创建,这些方式我也用过。# 方式1: 列表转换 行角度,需要手动指定列名 data1 = [ ['p1', '小米', 999], ['p2', '华为', 9999], ['p3', '苹果', 2], ['p4', '橘子', 3], ['p5', '菠萝', 2] ] df1 = pd.DataFrame(data1, columns=['id', 'name', 'price']) print(df1) # 方式2: 字典转换 列角度,默认key就是列名 data2 = { "id": ['p1', 'p2', 'p3', 'p4', 'p5'], "name": ['小米', '华为', '苹果', '橘子', '菠萝'], "price": [999, 9999, 2, 3, 2], }
六、熟悉DataFrame的相关操作:选择、筛选、缺失值、分组聚合
-
这些都是我日常分析的核心操作:
-
选择:
-
选列可以用
df[['列名1','列名2']],选行可以用df.loc[行索引]或者df.iloc[行号]。# 列表转换 行角度,需要手动指定列名 data1 = [ ['p1', '小米', 999, 100], ['p2', '华为', 9999, 100], ['p3', None, 2, 100], ['p4', pd.NA, 3, 100], ['p5', np.nan, 2, 100], ['p6', '香蕉', 2.5, 100], ] df1 = pd.DataFrame(data1, columns=['id', 'name', 'price','num']) # 需求: 查看数据信息 df1.info() print('--------------------------') print(df1.isnull().sum()) print('--------------------------') print(df1.describe()) print('============================') # 需求: 获取前3行数据 print(df1.head(3)) # 默认5行 # 需求: 获取最后2行数据 print(df1.tail(2)) # 默认5行 print('============================') # 需求: 查看商品价格 print(df1['price']) # 需求: 查看商品名称和价格 print(df1[['name', 'price']]) # 需求: 查看商品价格大于2元的商品信息 print(df1[df1['price'] > 2]) print('============================') # loc(): 根据标签找 # iloc(): 根据索引找 -
筛选:
-
比如筛选“年龄>20”的行用
df[df['年龄']>20],多条件筛选(比如“年龄>20且消费>100”)用df[(df['年龄']>20) & (df['消费']>100)]。import pandas as pd # 创建示例数据 df = pd.DataFrame({ '姓名': ['张三', '李四', '王五', '赵六', '钱七'], '年龄': [25, 30, 35, 25, 32], '部门': ['技术部', '销售部', '技术部', '人事部', '销售部'], '工资': [5000, 7000, 6000, 5500, 7500] }) print("原始数据:") print(df) print('==========================================') # 需求: 查询年龄大于30的员工信息 print(df[df['年龄'] > 30]) # 需求: 查询技术部员工信息 print(df[df['部门'] == '技术部']) # 需求: 查询年龄大于30并且是技术部员工信息 print(df.query('年龄 > 30 and 部门=="技术部"')) print('==========================================') # 需求: 按照工资降序序排序 print(df.sort_values('工资',ascending=False)) # 需求: 先按照年龄降序排序,然后按照工资降序排序 print(df.sort_values(['年龄','工资'],ascending=[False,False])) -
缺失值处理:
-
先用
df.isnull().sum()统计每列的空值数量,然后根据情况处理——比如用df.dropna()删除有空值的行,或者用df.fillna(df['列名'].mean())把空值填充成该列的均值。# 导包 import pandas as pd # 读取文件数据 df = pd.read_csv('data/清洗数据.csv', sep=',') print(df) # 检查缺失值个数 print(df.isnull().sum()) print(df.isna().sum()) print('==================================') # 删除缺失值 print(df.dropna()) # 默认只有当前行有缺失值就删除当前行 print(df.dropna(how='all')) # how='all'只有当前行都是缺失值就删除 print('==================================') # 填充缺失值 print(df.fillna(0)) print(df.fillna(df.mean())) -
分组聚合:
-
比如按“性别”分组求“消费”的均值,用
df.groupby('性别')['消费'].mean();如果要同时求均值和总和,就用df.groupby('性别')['消费'].agg(['mean','sum'])。# 导包 import pandas as pd # 读取数据 df = pd.read_csv('data/分组聚合数据.csv', sep=',') print(df) # 需求: 各个产品的总销售额 print(df.groupby('产品')['销售额'].sum()) print(df.groupby('产品')['销售额'].agg(['sum'])) # 需求: 各个产品在各个地区的总销售额和平均销售额以及最大最小销售额 print(df.groupby(['产品', '地区'])['销售额'].agg(['sum', 'mean', 'max', 'min'])) print('=====================================================') # 需求: 使用透视表完成上述需求 # 1.各个产品的总销售额 data = df.pivot_table(index='产品', values='销售额', aggfunc='sum') print(data) # 需求: 各个产品在各个地区的总销售额和平均销售额以及最大最小销售额 data = df.pivot_table(index=['产品', '地区'], values='销售额', aggfunc=['sum', 'mean', 'max', 'min']) print(data) -
数据透视表:
-
pivot_table()
七、能够使用Matplotlib绘制简单的折线图和散点图
这两种图我经常用:
-
折线图:比如画“日期-销量”的折线图,先导入库
import matplotlib.pyplot as plt,然后用plt.plot(df['日期'], df['销量']),再加上plt.xlabel('日期')(x轴标签)、plt.ylabel('销量')(y轴标签)、plt.title('每日销量趋势')(标题),最后用plt.show()显示图表。# 导包 import numpy as np import matplotlib.pyplot as plt import matplotlib matplotlib.use('TkAgg') # 准备数据 x = np.array([1, 2, 3, 4, 5]) y = np.array([100, 12, 23, 141, 50]) # 绘制折线图 plt.plot(x, y,color='red') plt.show() -
散点图:比如画“年龄-消费”的散点图,用
plt.scatter(df['年龄'], df['消费']),同样可以加标签和标题,还能通过c参数指定点的颜色(比如c='red'),通过s参数调整点的大小。
如果是从Pandas的DataFrame取数据,直接把Series传进去就行,不用额外转格式。
import numpy as np
import matplotlib.pyplot as plt
# 注意:新版本需要指定 matplotlib 使用 TkAgg 作为图形后端来渲染和显示图表
import matplotlib
matplotlib.use('TkAgg')
# 注意:中文显示需要额外设置字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 创建数据
np.random.seed(42) # 设置随机种子,保证结果可重复
x = np.random.randn(50)
# y = x * 2
y = x * 2 + np.random.randn(50) * 0.8 # 添加一些随机噪声
# 绘制散点图
plt.figure(figsize=(8, 5))
plt.scatter(x, y, color='red')
# 添加标题和标签
plt.title("散点图示例")
plt.xlabel("X值")
plt.ylabel("Y值")
# 显示网格
plt.grid()
# 显示图表
plt.show()
案例:绘制中美日三个国家的GDP数据每年变化折线图
# -*- coding: utf-8 -*-
import pandas as pd
# 处理中文乱码问题
import matplotlib
from matplotlib import pyplot as plt
matplotlib.use("TkAgg")
matplotlib.rcParams["font.sans-serif"] = ["SimHei"]
matplotlib.rcParams["axes.unicode_minus"] = False
# 解决GBK编码问题
# 需求:绘制中美日三个国家的GDP数据每年变化折线图
# 读取数据
df = pd.read_csv("../02_pandas/data/1960-2019全球GDP数据.csv", encoding="gbk")
print(df.shape)
# 数据预处理
df.dropna(inplace=True)
print(df.shape)
# 分别获取美国,中国,日本GDP数据
usa_gdp = df[df["country"] == "美国"].copy()
china_gdp = df[df["country"] == "中国"].copy()
japan_gdp = df[df["country"] == "日本"].copy()
# 设置列索引为年份
usa_gdp.set_index("year", inplace=True)
china_gdp.set_index("year", inplace=True)
japan_gdp.set_index("year", inplace=True)
# 数据可视化
plt.title("1960-2019年中美日三个国家GDP折线图")
plt.plot(usa_gdp.index, usa_gdp["GDP"], label="美国", color="green")
plt.plot(china_gdp.index, china_gdp["GDP"], label="中国", color="red")
plt.plot(japan_gdp.index, japan_gdp["GDP"], label="日本", color="blue")
plt.legend()
plt.show()
元数据:
更多推荐
所有评论(0)