这一章会快速介绍NumPy和Pandas。更加详细的数据清洗、处理和绘图,会在后续专门的章节进行讲解。
NumPy: 书店销售数据
NumPy用来处理多维数组,在一维的情况下,可以看成是一个超快的List。
以书店销售数据为背景,我们将依次介绍numpy的数组创建、操作、四则运算、聚合函数和布尔筛选。
数据准备
首先,我们从书店的基本数据开始,即书籍的价格和销售数量。
数组创建
从列表创建一维数组来记录书籍的价格和销售数量:
import numpy as np
# 书籍的单价列表
price_list = [120, 85, 100, 75, 95]
# 将列表转换为numpy数组
prices = np.array(price_list)
# 书籍的销售数量列表
quantity_list = [30, 15, 20, 10, 25]
# 将列表转换为numpy数组
quantities = np.array(quantity_list)
# 书籍名称
book_names = np.array(['微观经济学', '宏观经济学', '金融学', '会计学','Python数据分析'])
print("书籍价格数组:", prices)
print("销售数量数组:", quantities)
print("prices.shape:", prices.shape)
print("quantities.shape:", quantities.shape)
print("prices.dtype:", prices.dtype)
书籍价格数组: [120 85 100 75 95]
销售数量数组: [30 15 20 10 25]
prices.shape: (5,)
quantities.shape: (5,)
prices.dtype: int64
索引和切片
展示如何获取数组中的特定元素和子数组:本节演示如何访问数组的元素与子数组,常用语法包括 a[i]、a[i:j] 以及带步长的 a[i:j:k]。
# 获取第一本书的价格和销售数量
first_book_price = prices[0]
first_book_quantity = quantities[0]
print("第一本书的价格:", first_book_price)
print("第一本书的销售数量:", first_book_quantity)
# 获取前三本书的价格和销售数量
first_three_prices = prices[:3]
first_three_quantities = quantities[:3]
print("前三本书的价格:", first_three_prices)
print("前三本书的销售数量:", first_three_quantities)
第一本书的价格: 120
第一本书的销售数量: 30
前三本书的价格: [120 85 100]
前三本书的销售数量: [30 15 20]
布尔数组和筛选
创建布尔数组并筛选出价格高于90元的书籍:
先基于条件构造布尔掩码(如 prices > 90),再用 a[mask] 选取满足条件的元素。
# 创建一个布尔数组,用于筛选价格高于90元的书籍
high_price_filter = prices > 90
high_price_books = prices[high_price_filter]
print("价格高于90元的书籍:", high_price_books)
selected_names = book_names[high_price_filter]
print("价格>90元的书籍名称:", selected_names)
价格高于90元的书籍: [120 100 95]
价格>90元的书籍名称: ['微观经济学' '金融学' 'Python数据分析']
条件选择:np.where
利用 np.where 按条件进行向量化选择(条件为真取左项,否则取右项)。np.where(cond, x, y) 可按条件在两组值之间进行向量化选择:条件为真取 x,否则取 y。
labels = np.where(prices >= 100, "高价", "低价")
print("价格标签:", labels)
discounted_unit = np.where(prices >= 100, prices * 0.9, prices)
print("按条件折扣后的单价:", discounted_unit)
价格标签: ['高价' '低价' '高价' '低价' '低价']
按条件折扣后的单价: [108. 85. 90. 75. 95.]
数组运算
使用数组运算来计算总销售额和应用折扣。
基本数学运算
计算总销售额(未折扣前):
使用向量化运算符(+、-、*、/)完成逐元素计算,并配合 sum、mean、min、max 等聚合函数得到汇总结果。
# 计算每本书未折扣前的总销售额
total_sales = prices * quantities
print("未折扣前的总销售额:", total_sales)
print("sum(total_sales)=", total_sales.sum())
print("np.dot(prices, quantities)=", np.dot(prices, quantities))
未折扣前的总销售额: [3600 1275 2000 750 2375]
sum(total_sales)= 10000
np.dot(prices, quantities)= 10000
应用折扣
计算折后总价:
根据给定的折扣比率逐元素计算折后值,也可结合 np.where 按条件应用不同折扣。
# 书籍的折扣比率
discount_rates = np.array([0.1, 0.05, 0.15, 0.05, 0.1]) # 10%, 5%, 15%, 5%, 10%
final_prices = total_sales * (1 - discount_rates)
print("折扣后的销售额:", final_prices)
折扣后的销售额: [3240. 1211.25 1700. 712.5 2137.5 ]
常见函数
使用聚合和其他常见函数:展示常用汇总统计与极值定位,例如 sum、mean、min、max、argmin、argmax 等。
print("折后总销售额(合计):", final_prices.sum())
print("最低折后销售额:", final_prices.min())
print("最高折后销售额:", final_prices.max())
print("平均折后销售额:", final_prices.mean())
折后总销售额(合计): 9001.25
最低折后销售额: 712.5
最高折后销售额: 3240.0
平均折后销售额: 1800.25
练习题
题目 1: 价格调整
假设书店决定对所有书籍价格进行统一调整,增加5元。请使用numpy数组操作来更新prices数组,并打印新的价格数组。
题目 2: 销售筛选
书店想要了解哪些书籍的销售额在折扣后仍然超过2000元。使用布尔索引来筛选出这些书籍的价格和名称,并打印结果。
题目 3: 总结统计
请计算并打印以下统计信息:
- 所有书籍的销售总数。
- 折扣后每本书平均的销售额。
- 折扣率最高的书籍的名称和价格。
题目 4: 条件选择
使用 np.where 按价格阈值(如 100 元)为每本书生成“高价/低价”标签,并构造按条件折扣后的单价向量。
例如:基于当前 prices = np.array([120, 85, ... ]),标签可得到 ["高价", "低价", ... ],按条件折扣后的单价可得到 [108.0, 85.0, ...]。
Pandas: 书店数据管理
Pandas 常常用于处理二位表格数据,可以看成是一个超级Excel。
在这个例子中,我们将使用一个关于书店的数据集,包含书籍的名称、价格、销售数量和评分。通过这个例子,我们将演示如何使用pandas进行数据的创建、操作、排序和文件的读写。
导入pandas库
首先,导入pandas库并为其常用的别名pd:
创建DataFrame
使用字典来创建一个DataFrame,其中包括书籍的名称、价格、销售数量和评分:
通过字典或列表的字典构造表格数据(pd.DataFrame(…)),每列为一个带索引的 Series。
data = {
"Book": ['微观经济学', '宏观经济学', '金融学', '会计学','Python数据分析'],
"Price": [120, 85, 100, 75, 95],
"Quantity": [30, 15, 20, 10, 25],
"Rating": [4.5, 4.0, 4.8, 3.9, 4.1],
}
df = pd.DataFrame(data)
df
| 0 |
微观经济学 |
120 |
30 |
4.5 |
| 1 |
宏观经济学 |
85 |
15 |
4.0 |
| 2 |
金融学 |
100 |
20 |
4.8 |
| 3 |
会计学 |
75 |
10 |
3.9 |
| 4 |
Python数据分析 |
95 |
25 |
4.1 |
DataFrame 结构
变量df是一个DataFrame(约等于一个二维表格),可以视为列(Series)的横向合并。
其中,每个列,又是由一个索引index和数据values组成。
再其中,数据values是来自NumPy的array,因此pandas的列也可以享受到NumPy好处,比如广播和大量函数等等。

df['Book'] # 取一列,这是一个Series
0 微观经济学
1 宏观经济学
2 金融学
3 会计学
4 Python数据分析
Name: Book, dtype: object
df['Book'].values # 取一列,然后取出取其中的数据valus, 这是一个NumPy的array
array(['微观经济学', '宏观经济学', '金融学', '会计学', 'Python数据分析'], dtype=object)
df['Book'].index # 取一列,然后取出取其中中的index(索引)
RangeIndex(start=0, stop=5, step=1)
数据选择与操作
使用.loc
.loc主要用于基于标签的索引,即选择行或列的名称来访问数据。
选择特定的行通过标签或位置访问与选择数据:使用 loc(标签)、iloc(位置),也可用列名列表与切片。
# 选择第一本书的数据
first_book = df.loc[0]
print("第一本书的数据:\n")
print(first_book)
第一本书的数据:
Book 微观经济学
Price 120
Quantity 30
Rating 4.5
Name: 0, dtype: object
选择特定的列
选择若干列以聚焦关键信息,例如 df[[“Book”,“Price”]] 或 df.loc[:, [“Book”,“Price”]]。
# 选择书籍名称和价格列
books_prices = df.loc[:, ["Book", "Price"]]
print("书籍和价格:\n")
books_prices
| 0 |
微观经济学 |
120 |
| 1 |
宏观经济学 |
85 |
| 2 |
金融学 |
100 |
| 3 |
会计学 |
75 |
| 4 |
Python数据分析 |
95 |
也有快速的写法
# 选择书籍名称和价格列
books_prices = df[["Book", "Price"]]
print("书籍和价格:\n")
books_prices
| 0 |
微观经济学 |
120 |
| 1 |
宏观经济学 |
85 |
| 2 |
金融学 |
100 |
| 3 |
会计学 |
75 |
| 4 |
Python数据分析 |
95 |
使用.iloc
.iloc用于基于位置的索引,即选择行或列的数值索引来访问数据。
选择特定的行
iloc 按整数位置选择行列,可使用切片或整数列表获取多行多列。
# 选择前三本书的数据
first_three_books = df.iloc[:3]
print("前三本书的数据:\n")
first_three_books
| 0 |
微观经济学 |
120 |
30 |
4.5 |
| 1 |
宏观经济学 |
85 |
15 |
4.0 |
| 2 |
金融学 |
100 |
20 |
4.8 |
选择特定的列选择若干列以聚焦关键信息,例如 df[[“Book”,“Price”]] 或 df.loc[:, [“Book”,“Price”]]。
# 选择价格和数量列(索引1和2)
price_quantity = df.iloc[:, [1, 2]]
print("价格和数量:\n")
price_quantity
| 0 |
120 |
30 |
| 1 |
85 |
15 |
| 2 |
100 |
20 |
| 3 |
75 |
10 |
| 4 |
95 |
25 |
条件筛选
使用条件表达式来筛选满足特定条件的行。
根据条件选择行构造布尔表达式筛选行(如 df[“Rating”] > 4.0),多条件使用 & 或 | 并配合括号。
# 选择评分高于4.0的书籍
high_rated_books = df[df["Rating"] > 4.0]
print("评分高于4.0的书籍:\n")
high_rated_books
| 0 |
微观经济学 |
120 |
30 |
4.5 |
| 2 |
金融学 |
100 |
20 |
4.8 |
| 4 |
Python数据分析 |
95 |
25 |
4.1 |
使用query方法 这是一个更动态的方式来选择数据,允许使用字符串表达式来指定条件。query 以字符串表达式筛选,例如 df.query(“Price < 100”),变量可用 @var 引用。
# 使用query方法选择价格小于100的书籍
affordable_books = df.query("Price < 100")
print("价格小于100的书籍:\n")
affordable_books
| 1 |
宏观经济学 |
85 |
15 |
4.0 |
| 3 |
会计学 |
75 |
10 |
3.9 |
| 4 |
Python数据分析 |
95 |
25 |
4.1 |
修改数据列
添加一列“Total Sales”,计算每本书的总销售额:
直接用向量化表达式生成新列,例如 df[“Total Sales”] = df[“Price”] * df[“Quantity”]。
df["Total Sales"] = df["Price"] * df["Quantity"]
df
| 0 |
微观经济学 |
120 |
30 |
4.5 |
3600 |
| 1 |
宏观经济学 |
85 |
15 |
4.0 |
1275 |
| 2 |
金融学 |
100 |
20 |
4.8 |
2000 |
| 3 |
会计学 |
75 |
10 |
3.9 |
750 |
| 4 |
Python数据分析 |
95 |
25 |
4.1 |
2375 |
数据排序
根据总销售额对书籍进行降序排序:使用 sort_values 按指标排序,支持多列与升降序控制。
df_sorted = df.sort_values(by="Total Sales", ascending=False)
print("按总销售额排序:\n")
df_sorted
| 0 |
微观经济学 |
120 |
30 |
4.5 |
3600 |
| 4 |
Python数据分析 |
95 |
25 |
4.1 |
2375 |
| 2 |
金融学 |
100 |
20 |
4.8 |
2000 |
| 1 |
宏观经济学 |
85 |
15 |
4.0 |
1275 |
| 3 |
会计学 |
75 |
10 |
3.9 |
750 |
缺失值处理
演示缺失值检测与填充:先构造缺失,再使用 isna 计数与 fillna 填充。先检测缺失(isna / isnull),再按需求填充(fillna)或丢弃(dropna)。
df_na = df.copy()
df_na.loc[1, "Rating"] = None # 添加一个缺失值
df_na
| 0 |
微观经济学 |
120 |
30 |
4.5 |
3600 |
| 1 |
宏观经济学 |
85 |
15 |
NaN |
1275 |
| 2 |
金融学 |
100 |
20 |
4.8 |
2000 |
| 3 |
会计学 |
75 |
10 |
3.9 |
750 |
| 4 |
Python数据分析 |
95 |
25 |
4.1 |
2375 |
df_na["Rating"].isna() # 问,Rating这一列,谁是缺失值?
0 False
1 True
2 False
3 False
4 False
Name: Rating, dtype: bool
# 上一个单元格中的boo序列是可以求和的:True被视为1,False被视为0,等于问,有多少个True
print("Rating 缺失值个数:", df_na["Rating"].isna().sum())
df_na["Rating"] = df_na["Rating"].fillna(df_na["Rating"].mean())
df_na
| 0 |
微观经济学 |
120 |
30 |
4.500 |
3600 |
| 1 |
宏观经济学 |
85 |
15 |
4.325 |
1275 |
| 2 |
金融学 |
100 |
20 |
4.800 |
2000 |
| 3 |
会计学 |
75 |
10 |
3.900 |
750 |
| 4 |
Python数据分析 |
95 |
25 |
4.100 |
2375 |
数据合并
concat 用于按行或按列堆叠数据(理解为把2个数据横向或者纵向粘贴在一起,对齐index);merge 用于按键连接表(以某一列(或者多列)为依据,合并数据)。
# 构造一个新的行:一本新书
df_new = pd.DataFrame({
"Book": ["计量经济学"],
"Price": [110],
"Quantity": [12],
"Rating": [4.2],
})
df_new
# concat接受一个list,其中是你要合并的2个DataFrame或者Series
# 默认是纵向合并(添加行,或者说,添加样本/观测值)
df_concat = pd.concat([df, df_new], ignore_index=True)
df_concat
| 0 |
微观经济学 |
120 |
30 |
4.5 |
3600.0 |
| 1 |
宏观经济学 |
85 |
15 |
4.0 |
1275.0 |
| 2 |
金融学 |
100 |
20 |
4.8 |
2000.0 |
| 3 |
会计学 |
75 |
10 |
3.9 |
750.0 |
| 4 |
Python数据分析 |
95 |
25 |
4.1 |
2375.0 |
| 5 |
计量经济学 |
110 |
12 |
4.2 |
NaN |
# 构造一个新的数据:出版社
df_info = pd.DataFrame({
"Book": ["微观经济学", "金融学", "大学语文"],
"Publisher": ["A社", "B社", "C社"],
})
df_info
| 0 |
微观经济学 |
A社 |
| 1 |
金融学 |
B社 |
| 2 |
大学语文 |
C社 |
# 把新数据merge到原数据中
# on: 按什么列来合并,可以取多列,后面要用到
# how:哪个表为依据:left 即完全保留左侧的表(df),把左侧表中有的样本,找到右侧表对应的数据合并进来,左表没有的样本就不管。
df_merge = df.merge(df_info, on="Book", how="left")
df_merge
| 0 |
微观经济学 |
120 |
30 |
4.5 |
3600 |
A社 |
| 1 |
宏观经济学 |
85 |
15 |
4.0 |
1275 |
NaN |
| 2 |
金融学 |
100 |
20 |
4.8 |
2000 |
B社 |
| 3 |
会计学 |
75 |
10 |
3.9 |
750 |
NaN |
| 4 |
Python数据分析 |
95 |
25 |
4.1 |
2375 |
NaN |
读取和写入数据
写入CSV文件
将DataFrame写入CSV文件,文件名为bookstore.csv:将数据保存为 CSV(to_csv)并用 read_csv 读回以便后续分析。
df.to_csv("bookstore.csv", index=False)
print("数据已写入 'bookstore.csv'")
读取CSV文件 读取之前写入的CSV文件,查看内容:
df_loaded = pd.read_csv("bookstore.csv")
print("从CSV文件加载的数据:\n")
df_loaded
| 0 |
微观经济学 |
120 |
30 |
4.5 |
3600 |
| 1 |
宏观经济学 |
85 |
15 |
4.0 |
1275 |
| 2 |
金融学 |
100 |
20 |
4.8 |
2000 |
| 3 |
会计学 |
75 |
10 |
3.9 |
750 |
| 4 |
Python数据分析 |
95 |
25 |
4.1 |
2375 |
练习题
题目 1: 计算平均评分
假设你想了解书店中所有书籍的平均评分。使用pandas的函数计算并打印这个数据集中所有书籍的平均评分。
题目 2: 筛选特定的书籍
书店老板对销售表现不佳的书籍(总销售额低于2000元)进行特价处理。请筛选出这些书籍的名称和当前价格,并打印结果。
题目 3: 新书上架 书店有新书上架,需要更新DataFrame。请将下面的新书信息添加到现有的DataFrame中,并重新打印更新后的DataFrame。
新书信息:
- Book: ‘计量经济学’
- Price: 90
- Quantity: 20
- Rating: 4.3
题目 4: 添加出版社信息
给定下方字典 publisher_info,请将其并入现有 DataFrame df,使结果包含新列 Publisher。请自行选择合适的连接方式与参数。
publisher_info = {
"Book": ["微观经济学", "宏观经济学", "金融学", "会计学", "Python数据分析", "计量经济学"],
"Publisher": ["A社", "B社", "B社", "C社", "D社", "E社"],
}