数据分析三剑客:

一、能说出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()

  1. 这块我用得很多:

  • 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对象的创建操作:从文件读取或者列表/字典等转换

  1. 这些创建方式我都常用:

  • 从文件读取:比如读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的相关操作:选择、筛选、缺失值、分组聚合

  1. 这些都是我日常分析的核心操作:

  • 选择

  • 选列可以用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()

元数据:

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐