Python Pandas基础100题精练及详解
Pandas 数据分析 100 道基础练习题(附解析)
第1-20 题:数据创建、查看与基础操作
1. 导入 Pandas 和 NumPy 库,并打印它们的版本信息。
import pandas as pd
import numpy as np
print(f"Pandas version: {pd.__version__}")
print(f"NumPy version: {np.__version__}")
解析:这是使用 Pandas 和 NumPy 的第一步,通过 __version__ 属性可以确认库的版本,确保环境兼容性。
2. 从字典创建一个 DataFrame,字典内容为:{'A': [1, 2, 3], 'B': [4, 5, 6], 'C': [7, 8, 9]}。
data = {'A': [1, 2, 3], 'B': [4, 5, 6], 'C': [7, 8, 9]}
df = pd.DataFrame(data)
print(df)
解析:pd.DataFrame() 是创建 DataFrame 最常用的方法之一,可以直接将 Python 字典转换为表格结构,字典的键成为列名,值成为列数据。
3. 从 NumPy 数组创建一个 DataFrame,数组为 np.arange(12).reshape(3,4),列名为 ['W', 'X', 'Y', 'Z']。
import numpy as np
arr = np.arange(12).reshape(3,4)
df = pd.DataFrame(arr, columns=['W', 'X', 'Y', 'Z'])
print(df)
解析:pd.DataFrame() 也可以直接接收二维数组,并通过 columns 参数指定列名。np.arange(12).reshape(3,4) 生成一个 3行4列,从0到11的数组。
4. 读取名为 'data.csv' 的 CSV 文件到 DataFrame 中。
df = pd.read_csv('data.csv')
解析:pd.read_csv() 是读取 CSV 文件的标准函数。默认第一行为列名。可以添加 encoding, sep 等参数处理不同格式的文件。
5. 查看上述 DataFrame 的前 3 行数据。
print(df.head(3))
解析:df.head(n) 方法用于查看 DataFrame 的前 n 行,默认 n=5。这是数据探索的第一步,用于快速了解数据结构。
6. 查看上述 DataFrame 的后 5 行数据。
print(df.tail())
解析:df.tail(n) 方法用于查看 DataFrame 的后 n 行,默认 n=5。常用于检查数据末尾的格式或内容。
7. 查看 DataFrame 的索引、列名、数据类型和非空值数量等摘要信息。
print(df.info())
解析:df.info() 是数据预览的核心方法,能快速了解数据形状、每列数据类型及内存占用,是发现数据问题的第一步。
8. 查看 DataFrame 的维度(行数和列数)。
print(df.shape)
解析:df.shape 返回一个元组 (行数, 列数),是获取数据规模最直接的方式。
9. 查看 DataFrame 的列名。
print(df.columns)
解析:df.columns 返回一个 Index 对象,包含所有列名。了解列名是进行数据选择和操作的前提。
10. 查看 DataFrame 的索引。
print(df.index)
解析:df.index 返回行索引信息。默认是 RangeIndex,也可以被设置为时间序列或其他类型。
11. 将 DataFrame 保存为名为 'output.xlsx' 的 Excel 文件。
df.to_excel('output.xlsx', index=False) # index=False 表示不保存行索引
解析:df.to_excel() 用于将 DataFrame 写入 Excel 文件。index=False 是常用参数,避免将行索引作为额外一列写入。
12. 仅查看 DataFrame 中数值类型列(int, float)的统计摘要(如计数、均值、标准差、最小值、四分位数、最大值)。
print(df.describe())
解析:df.describe() 默认只对数值列进行统计,生成计数、均值、标准差、最小值、25%/50%/75%分位数和最大值,是数据分布探索的关键工具。
13. 查看 DataFrame 中所有列(包括对象类型)的统计摘要。
print(df.describe(include='all'))
解析:describe(include='all') 会对所有列进行统计。对于非数值列,它会显示唯一值数量、出现最频繁的值及其频次等。
14. 计算 DataFrame 中每一列的非空值数量。
print(df.count())
解析:df.count() 返回每列非空(非NaN)值的数量,是检查数据完整性的基本方法。
15. 计算 DataFrame 中每一列的空值(NaN)数量。
print(df.isnull().sum())
解析:df.isnull() 返回一个布尔型 DataFrame,指示每个元素是否为空。再使用 .sum() 对每列的 True 值(即空值)进行求和。
16. 删除 DataFrame 中任何包含空值的行。
df_cleaned = df.dropna()
print(df_cleaned.shape)
解析:df.dropna() 默认删除任何包含至少一个空值的行。可通过 axis, how, thresh 等参数控制删除行为。
17. 删除 DataFrame 中 ‘col1’ 和 ‘col2’ 这两列。
df_dropped = df.drop(['col1', 'col2'], axis=1)
print(df_dropped.columns)
解析:df.drop() 用于删除行或列。指定列名列表和 axis=1(或 columns=['col1', 'col2'])表示删除列。axis=0 表示删除行。
18. 将 DataFrame 的索引重置为从 0 开始的连续整数,并将原来的索引作为新的一列 ‘old_index’。
df_reset = df.reset_index(names='old_index')
print(df_reset.head())
解析:df.reset_index() 将当前索引变为普通列,并新建一个默认的整数索引。names 参数可以为被重置的旧索引列命名。
19. 将 DataFrame 的 ‘date’ 列设置为索引。
df_indexed = df.set_index('date')
print(df_indexed.head())
解析:df.set_index() 将指定列设置为新的行索引,常用于时间序列分析,便于基于时间的切片和重采样。
20. 创建一个 Series,数据为 [10, 20, 30, 40],索引为 [‘a’, ‘b’, ‘c’, ‘d’]。
s = pd.Series([10, 20, 30, 40], index=['a', 'b', 'c', 'd'])
print(s)
解析:Series 是带标签的一维数组。pd.Series(data, index) 是创建 Series 的基本方法,索引标签可用于数据选择。
第 21-40 题:数据选择与筛选
21. 使用 iloc 选择 DataFrame 的第 2 行(索引为 1)数据。
row_2 = df.iloc[1]
print(row_2)
解析:df.iloc[] 基于整数位置进行选择。df.iloc[1] 选择第二行(从0开始计数),返回一个 Series。
22. 使用 loc 选择索引标签为 ‘row2’ 的行数据。
row_label = df.loc['row2']
print(row_label)
解析:df.loc[] 基于索引标签进行选择。前提是索引包含标签 ‘row2’。如果索引是默认整数,则 df.loc[1] 与 df.iloc[1] 效果相同。
23. 使用 iloc 选择 DataFrame 的第 1 到第 3 行(不包含第3行),以及第 0 和第 2 列。
subset = df.iloc[0:2, [0, 2]] # 行切片 0:2 表示 0,1;列列表 [0,2] 表示第1和第3列
print(subset)
解析:iloc 支持行和列的切片与列表选择。切片 0:2 遵循“左闭右开”原则。列选择 [0, 2] 表示一个索引列表。
24. 使用 loc 选择索引标签从 ‘A’ 到 ‘C’ 的行,以及列名为 [‘col1’, ‘col3’] 的列。
subset = df.loc['A':'C', ['col1', 'col3']] # 注意标签切片 'A':'C' 是闭区间
print(subset)
解析:loc 的标签切片是闭区间,即包含起始和结束标签。这与 iloc 的整数切片不同。
25. 选择 DataFrame 中名为 ‘age’ 的单个列。
age_series = df['age'] # 或 df.age (不推荐,当列名与DataFrame方法冲突时会出错)
print(type(age_series)) # 输出:<class 'pandas.core.series.Series'>
解析:使用方括号 df[‘col_name’] 是选择单列的标准方式,返回一个 Series。点号语法 df.col_name 虽然方便,但不够健壮。
26. 选择 DataFrame 中名为 [‘name’, ‘score’] 的多列。
multi_cols = df[['name', 'score']]
print(type(multi_cols)) # 输出:<class 'pandas.core.frame.DataFrame'>
解析:在方括号内传入一个列名的列表,可以选择多列,返回一个新的 DataFrame。
27. 筛选出 ‘score’ 列大于 90 的所有行。
high_scores = df[df['score'] > 90]
print(high_scores)
解析:df[‘score’] > 90 会生成一个布尔型 Series。将其放入 df[] 中,会返回所有对应位置为 True 的行。这是 Pandas 中最核心的布尔索引操作。
28. 筛选出 ‘department’ 列等于 ‘Sales’ 且 ‘salary’ 大于 50000 的所有行。
filtered = df[(df['department'] == 'Sales') & (df['salary'] > 50000)]
print(filtered)
解析:多个条件组合时,每个条件必须用括号 () 括起来,并使用位运算符 & (与)、| (或)、~ (非) 连接,不能使用 and, or, not。
29. 筛选出 ‘city’ 列在列表 [‘Beijing’, ‘Shanghai’, ‘Guangzhou’] 中的行。
cities = ['Beijing', 'Shanghai', 'Guangzhou']
in_cities = df[df['city'].isin(cities)]
print(in_cities)
解析:Series.isin(list) 方法用于检查 Series 的每个元素是否存在于给定的列表中,返回布尔 Series,常用于多值筛选。
30. 筛选出 ‘email’ 列包含字符串 ‘@gmail.com’ 的所有行。
gmail_users = df[df['email'].str.contains('@gmail.com', na=False)]
print(gmail_users)
解析:Series.str.contains(pattern) 用于字符串匹配。na=False 参数确保当遇到空值(NaN)时返回 False 而不是 NaN,避免筛选错误。
31. 使用 query 方法筛选出 age 大于 30 且 gender 为 ‘M’ 的行。
filtered_query = df.query('age > 30 and gender == "M"')
print(filtered_query)
解析:df.query() 方法允许使用字符串表达式进行查询,语法更接近自然语言。注意字符串常量需要用双引号。
32. 随机从 DataFrame 中抽取 5 行数据(不放回)。
sampled = df.sample(n=5, random_state=42) # random_state 确保结果可复现
print(sampled)
解析:df.sample() 用于随机抽样。n 指定抽样数量,random_state 设置随机种子以保证结果可重复。
33. 提取 DataFrame 中 ‘value’ 列最大的前 3 行。
top3 = df.nlargest(3, 'value')
print(top3)
解析:df.nlargest(n, columns) 返回指定列值最大的前 n 行。对应的 df.nsmallest() 返回最小的前 n 行。比先排序再取头部更直观。
34. 选择 DataFrame 中所有数据类型为 ‘object’的列。
object_cols = df.select_dtypes(include=['object'])
print(object_cols.columns)
解析:df.select_dtypes() 根据数据类型选择列。include 参数指定要包含的类型,exclude 指定要排除的类型。
35. 使用 at 方法快速获取索引为 5、列名为 ‘price’ 的单个值。
value = df.at[5, 'price']
print(value)
解析:df.at[] 和 df.iat[] 用于快速访问单个标量值,速度比 loc/iloc 更快。at 基于标签,iat 基于整数位置。
36. 使用 iat 方法快速获取第 0 行、第 2 列(从0计数)的单个值。
value = df.iat[0, 2]
print(value)
解析:df.iat[0, 2] 等价于 df.iloc[0, 2],但 iat 是专门为访问单个标量优化的方法。
37. 筛选出 ‘col1’ 列的值不在另一个 Series s 中的行。
# 假设 s 是一个 Series
not_in_s = df[~df['col1'].isin(s)]
print(not_in_s)
解析:~ 是位取反运算符。df[‘col1’].isin(s) 返回布尔 Series,~ 将其反转,从而选择不在 Series s 中的行。
38. 提取 ‘category’ 列中出现频率最高的值。
most_frequent = df['category'].mode()[0] # mode() 返回一个Series,可能有多值,取第一个
print(most_frequent)
解析:Series.mode() 返回众数,即出现频率最高的值。由于可能存在多个众数,它返回一个 Series,通常取第一个元素 [0]。
39. 提取 ‘id’ 列中能被 5 整除的所有值。
divisible_by_5 = df[df['id'] % 5 == 0]['id']
print(divisible_by_5)
解析:利用取模运算符 % 和布尔索引。df[‘id’] % 5 == 0 生成布尔掩码,筛选出行后再选择 ‘id’ 列。
40. 计算 ‘value’ 列中每个值与其前一行的差值(第一行差值为 NaN)。
df['value_diff'] = df['value'].diff()
print(df[['value', 'value_diff']].head())
解析:Series.diff(periods=1) 计算当前元素与前一个元素的差值,是时间序列分析中计算增量的常用方法。periods 参数可调整间隔。
第 41-60 题:数据清洗与转换
41. 将 DataFrame 的列名 [‘A’, ‘B’, ‘C’] 改为 [‘X’, ‘Y’, ‘Z’]。
df.rename(columns={'A': 'X', 'B': 'Y', 'C': 'Z'}, inplace=True)
print(df.columns)
解析:df.rename(columns=dict) 用于重命名列。inplace=True 表示直接修改原 DataFrame,否则会返回一个新 DataFrame。
42. 将 DataFrame 的索引名改为 ‘ID’。
df.index.name = 'ID'
print(df.index.name)
解析:DataFrame 的索引可以有一个名字,通过 df.index.name 属性直接设置或修改。
43. 将 DataFrame 中 ‘amount’ 列的数据类型从 ‘object’ 转换为 ‘float’。
df['amount'] = df['amount'].astype(float)
print(df['amount'].dtype)
解析:Series.astype(dtype) 是转换数据类型的主要方法。转换失败(如字符串包含非数字字符)会报错,需先处理。
44. 将 ‘date_string’ 列(格式如 ‘2023-01-15’)转换为 Pandas 的 datetime 类型。
df['date'] = pd.to_datetime(df['date_string'])
print(df['date'].dtype)
解析:pd.to_datetime() 是解析日期时间字符串的强大函数,能自动识别多种格式。转换后便于进行时间序列操作。
45. 将 ‘price’ 列中所有大于 100 的值替换为字符串 ‘High’。
df.loc[df['price'] > 100, 'price'] = 'High'
print(df['price'].unique())
解析:使用 df.loc[布尔条件, 列名] 定位到满足条件的特定单元格,然后进行赋值修改。
46. 使用 ‘Unknown’ 填充 DataFrame 中所有空值(NaN)。
df_filled = df.fillna('Unknown')
print(df_filled.isnull().sum())
解析:df.fillna(value) 用指定值填充所有空值。也可以传入字典为不同列指定不同的填充值,如 df.fillna({‘col1’: 0, ‘col2’: ‘missing’})。
47. 使用 ‘salary’ 列的平均值填充该列的空值。
mean_salary = df['salary'].mean()
df['salary'].fillna(mean_salary, inplace=True)
解析:这是处理数值列缺失值的常见策略。也可以使用中位数 .median() 或众数 .mode()[0] 进行填充。
48. 删除 DataFrame 中 ‘col1’ 列完全为空(全部为 NaN)的行。
df_dropped = df.dropna(subset=['col1'], how='all')
解析:df.dropna(subset=[列列表]) 只检查指定列是否有空值。how=‘all’ 表示只有当指定列全部为空时才删除该行。
49. 删除 DataFrame 中重复的行(基于所有列判断)。
df_deduped = df.drop_duplicates()
print(f"原始行数: {len(df)}, 去重后行数: {len(df_deduped)}")
解析:df.drop_duplicates() 默认基于所有列判断重复,保留第一次出现的行。可用 subset 参数指定依据列,keep 参数控制保留哪一行。
50. 删除 DataFrame 中在 [‘col1’, ‘col2’] 这两列上重复的行,只保留最后一次出现的行。
df_deduped_last = df.drop_duplicates(subset=['col1', 'col2'], keep='last')
解析:subset 指定判断重复的列组合。keep=‘last’ 保留最后一次出现的行,keep=False 会删除所有重复行。
51. 将 DataFrame 中 ‘status’ 列的值进行映射:{1: ‘Active’, 0: ‘Inactive’}。
mapping = {1: 'Active', 0: 'Inactive'}
df['status_label'] = df['status'].map(mapping)
print(df[['status', 'status_label']].head())
解析:Series.map(dict_or_series) 是进行值映射的便捷方法。它根据映射关系将 Series 中的每个值替换为新值。
52. 将 DataFrame 中 ‘grade’ 列的值使用 apply 方法转换为大写。
df['grade_upper'] = df['grade'].apply(lambda x: x.upper() if isinstance(x, str) else x)
print(df[['grade', 'grade_upper']].head())
解析:Series.apply(func) 将函数应用于 Series 的每个元素。这里使用 lambda 函数和条件判断,确保只对字符串类型应用 .upper()。
53. 使用 cut 函数将 ‘age’ 列分为 3 个区间:(0, 18], (18, 60], (60, 100],并命名为 [‘Young’, ‘Adult’, ‘Senior’]。
bins = [0, 18, 60, 100]
labels = ['Young', 'Adult', 'Senior']
df['age_group'] = pd.cut(df['age'], bins=bins, labels=labels, right=True)
print(df[['age', 'age_group']].head())
解析:pd.cut() 用于将连续数值分箱(离散化)。bins 定义区间边界,labels 定义区间标签,right=True 表示区间右闭(如 (0,18])。
54. 使用 qcut 函数将 ‘income’ 列按分位数分为 4 组(四分位)。
df['income_quartile'] = pd.qcut(df['income'], q=4, labels=['Q1', 'Q2', 'Q3', 'Q4'])
print(df['income_quartile'].value_counts())
解析:pd.qcut() 基于样本分位数进行分箱,确保每个分组有大致相同数量的数据点。q=4 表示分为4组(四分位)。
55. 对 DataFrame 按 ‘department’ 列进行分组,并计算每组 ‘salary’ 的平均值。
avg_salary_by_dept = df.groupby('department')['salary'].mean()
print(avg_salary_by_dept)
解析:df.groupby(‘分组列’)[‘计算列’].聚合函数() 是分组聚合的标准模式。.mean() 是聚合函数之一。
56. 对 DataFrame 按 [‘year’, ‘month’] 进行分组,并计算每组中 ‘sales’ 的总和与 ‘profit’ 的平均值。
grouped = df.groupby(['year', 'month']).agg({'sales': 'sum', 'profit': 'mean'})
print(grouped.head())
解析:df.groupby().agg(dict) 可以对不同的列应用不同的聚合函数。字典的键是列名,值可以是聚合函数字符串(如 ‘sum’)或函数对象。
57. 在分组后,重置索引,使分组键(‘year’, ‘month’)重新变为普通列。
grouped_reset = grouped.reset_index()
print(grouped_reset.head())
解析:groupby 操作后,分组键默认成为结果的索引。reset_index() 将其还原为普通列,便于后续合并或分析。
58. 计算 ‘col1’ 列与 ‘col2’ 列之间的欧氏距离(假设每行是一个点)。
import numpy as np
df['euclidean_dist'] = np.sqrt((df['col1'] - df['col2']) ** 2)
print(df[['col1', 'col2', 'euclidean_dist']].head())
解析:欧氏距离公式为 sqrt((x1-x2)^2)。这里利用 Pandas Series 的向量化运算,直接对整个列进行计算,无需循环。
59. 将 DataFrame 的列顺序从 [‘A’, ‘B’, ‘C’, ‘D’] 改为 [‘D’, ‘A’, ‘C’, ‘B’]。
df = df[['D', 'A', 'C', 'B']]
print(df.columns)
解析:通过使用包含新顺序列名的列表对 DataFrame 进行索引,可以重新排列列的顺序。
60. 交换 DataFrame 中 ‘col1’ 和 ‘col2’ 两列的位置。
col_names = list(df.columns)
idx1, idx2 = col_names.index('col1'), col_names.index('col2')
col_names[idx1], col_names[idx2] = col_names[idx2], col_names[idx1]
df = df[col_names]
print(df.columns)
解析:通过获取列名列表,交换目标列名的索引位置,然后按新列表重新索引 DataFrame,实现列位置交换。
第 61-80 题:数据统计、排序与聚合
61. 按 ‘score’ 列对 DataFrame 进行降序排序。
df_sorted = df.sort_values(by='score', ascending=False)
print(df_sorted.head())
解析:df.sort_values(by, ascending) 是主要的排序方法。by 指定排序列,ascending=False 表示降序。
62. 按 [‘dept’, ‘salary’] 进行排序,先按 ‘dept’ 升序,再按 ‘salary’ 降序。
df_sorted_multi = df.sort_values(by=['dept', 'salary'], ascending=[True, False])
print(df_sorted_multi.head())
解析:by 参数可以接收列名列表,实现多列排序。ascending 参数也对应地接收一个布尔值列表,分别指定每列的排序方向。
63. 计算 ‘age’ 列的中位数。
median_age = df['age'].median()
print(f"年龄中位数: {median_age}")
解析:Series.median() 计算中位数,对异常值不敏感,是描述数据集中趋势的稳健指标。
64. 计算 ‘salary’ 列的总和与平均值。
salary_sum = df['salary'].sum()
salary_mean = df['salary'].mean()
print(f"总和: {salary_sum}, 平均值: {salary_mean}")
解析:sum() 和 mean() 是基本的描述性统计方法。对于数值列,它们忽略 NaN 值。
65. 计算 ‘col1’ 列的标准差和方差。
std_dev = df['col1'].std()
variance = df['col1'].var()
print(f"标准差: {std_dev}, 方差: {variance}")
解析:std() 计算样本标准差(默认分母为 n-1),var() 计算样本方差。它们是衡量数据离散程度的重要指标。
66. 计算 ‘value’ 列的偏度(Skewness)和峰度(Kurtosis)。
skewness = df['value'].skew()
kurtosis = df['value'].kurt()
print(f"偏度: {skewness:.2f}, 峰度: {kurtosis:.2f}")
解析:偏度衡量数据分布的不对称性(正偏表示右尾长)。峰度衡量分布形态的陡峭程度(与正态分布相比)。
67. 计算 ‘category’ 列中每个唯一值的出现次数。
value_counts = df['category'].value_counts()
print(value_counts)
解析:Series.value_counts() 返回唯一值及其计数,默认按计数降序排列。是分类数据探索的必备工具。
68. 计算 ‘category’ 列中每个唯一值的出现频率(百分比)。
value_counts_pct = df['category'].value_counts(normalize=True) * 100
print(value_counts_pct)
解析:value_counts(normalize=True) 返回的是比例(总和为1),乘以100即得到百分比。
69. 对 DataFrame 按 ‘group’ 列分组后,计算每组的大小(行数)。
group_sizes = df.groupby('group').size()
print(group_sizes)
解析:groupby().size() 返回每个分组中的行数。与之类似的 groupby().count() 返回的是每列的非空值计数。
70. 计算 ‘sales’ 列的累计和。
df['cumulative_sales'] = df['sales'].cumsum()
print(df[['sales', 'cumulative_sales']].head())
解析:Series.cumsum() 计算累计和。类似的还有 cumprod() (累计积)、cummax() (累计最大值)、cummin() (累计最小值)。
71. 计算 ‘price’ 列的 3 期简单移动平均(例如,当前行与前两行的平均值)。
df['price_ma_3'] = df['price'].rolling(window=3, min_periods=1).mean()
print(df[['price', 'price_ma_3']].head())
解析:Series.rolling(window).mean() 计算滚动窗口平均值,常用于平滑时间序列数据。min_periods=1 表示即使窗口内数据不足也进行计算。
72. 找出 ‘value’ 列中的局部最大值(即比前一个和后一个值都大的点)。
# 方法:比较当前值是否大于前一个值和后一个值
local_max = df[(df['value'] > df['value'].shift(1)) & (df['value'] > df['value'].shift(-1))]
print(local_max[['value']])
解析:使用 shift(1) 获取前一行的值,shift(-1) 获取后一行的值。通过布尔索引找出同时大于两者的点。
73. 计算 DataFrame 中每列的平均值。
col_means = df.mean(numeric_only=True) # numeric_only=True 只计算数值列
print(col_means)
解析:df.mean() 默认对每列进行计算,返回一个 Series。numeric_only=True 是 Pandas 2.0 后的推荐写法,避免对非数值列计算报错。
74. 计算 DataFrame 中每行的最大值。
row_max = df.max(axis=1, numeric_only=True)
print(row_max.head())
解析:axis=1 表示按行进行计算。numeric_only=True 确保只考虑数值列。
75. 对 ‘data’ 列应用自定义函数:如果值大于100 则标记为 ‘High’,否则标记为 ‘Normal’。
def classify_value(x):
return 'High' if x > 100 else 'Normal'
df['data_class'] = df['data'].apply(classify_value)
print(df[['data', 'data_class']].head())
解析:apply 可以应用自定义函数。这里定义了一个简单的分类函数,对 Series 的每个元素进行判断并返回标签。
76. 使用 pivot_table 创建数据透视表,以 ‘Year’ 为行,‘Product’ 为列,‘Sales’ 为值,聚合函数为求和。
pivot = pd.pivot_table(df, values='Sales', index='Year', columns='Product', aggfunc='sum', fill_value=0)
print(pivot)
解析:pd.pivot_table() 是创建数据透视表的强大工具。values 指定要聚合的列,index 和 columns 指定行和列的分组键,aggfunc 指定聚合函数,fill_value 填充缺失值。
77. 使用 crosstab 计算 ‘Gender’ 和 ‘Department’ 的交叉频数表。
cross_tab = pd.crosstab(df['Gender'], df['Department'])
print(cross_tab)
解析:pd.crosstab() 专门用于计算两个或多个因子的简单交叉表(频数统计)。比 pivot_table 在计算频数时更简洁。
78. 计算 ‘col1’ 和 ‘col2’ 两列之间的相关系数。
correlation = df['col1'].corr(df['col2'])
print(f"相关系数: {correlation:.2f}")
解析:Series.corr(other_series) 计算皮尔逊相关系数,衡量两个变量间的线性相关程度(-1 到 1 之间)。
79. 计算整个 DataFrame 数值列之间的相关系数矩阵。
corr_matrix = df.corr(numeric_only=True)
print(corr_matrix)
解析:df.corr() 计算 DataFrame 中所有数值列两两之间的相关系数,返回一个对称矩阵。常用于探索特征间的关系。
80. 对 ‘amount’ 列进行标准化(Z-score 标准化):(x - mean) / std。
df['amount_zscore'] = (df['amount'] - df['amount'].mean()) / df['amount'].std()
print(df[['amount', 'amount_zscore']].head())
解析:Z-score 标准化将数据转换为均值为0、标准差为 1 的分布。这是许多机器学习算法的常见预处理步骤。
第 81-100 题:高级操作与综合应用
81. 将两个 DataFrame df1 和 df2 按行上下拼接(要求列相同)。
df_concat = pd.concat([df1, df2], ignore_index=True)
print(df_concat.shape)
解析:pd.concat([df1, df2], axis=0) 是沿行方向(axis=0)拼接多个 DataFrame。ignore_index=True 会重置索引,生成新的连续整数索引。
82. 将两个 DataFrame df1 和 df2 按列左右拼接(要求行索引相同)。
df_concat_col = pd.concat([df1, df2], axis=1)
print(df_concat_col.shape)
解析:axis=1 表示沿列方向拼接。要求两个 DataFrame 的行索引对齐,否则会产生 NaN。
83. 基于 ‘key’ 列,使用 merge 对 df_left 和 df_right 进行内连接(inner join)。
df_merged = pd.merge(df_left, df_right, on='key', how='inner')
print(df_merged.head())
解析:pd.merge() 是类似 SQL JOIN 的合并操作。on 指定连接键,how=‘inner’ 表示内连接,只保留两个 DataFrame 中都有的键。
84. 基于 [‘key1’, ‘key2’] 两列,对 df_left 和 df_right 进行左连接(left join)。
df_left_merged = pd.merge(df_left, df_right, on=['key1', 'key2'], how='left')
print(df_left_merged.head())
解析:on 参数可以接收一个列名列表,实现多键连接。how=‘left’ 表示左连接,保留左表所有行,右表匹配不上的填充 NaN。
85. 使用 join 方法,基于索引将 df1 和 df2 连接起来。
df_joined = df1.join(df2, how='inner')
print(df_joined.head())
解析:df1.join(df2) 默认基于索引进行连接,是 merge 的一个便捷特例。how 参数同样控制连接类型。
86. 将 ‘date’ 列设置为索引后,提取 2023 年 1 月份的所有数据。
df_date_index = df.set_index('date')
jan_2023_data = df_date_index['2023-01']
print(jan_2023_data.head())
解析:当索引是 DatetimeIndex 时,可以使用部分字符串索引进行便捷的切片,如 ‘2023-01’ 选择整个一月份的数据。
87. 对以日期为索引的 DataFrame,按周(‘W’)重采样并计算每周的 ‘sales’ 总和。
weekly_sales = df.resample('W', on='date')['sales'].sum()
print(weekly_sales.head())
解析:df.resample(freq) 是时间序列重采样的核心方法。‘W’ 表示按周(周日为每周最后一天)。on 参数指定日期时间列(如果该列不是索引)。
88. 将 ‘long_format_df’从长格式转换为宽格式,其中 ‘date’ 为索引,‘variable’ 列的值成为新列名,‘value’ 列的值成为单元格值。
df_wide = long_format_df.pivot(index='date', columns='variable', values='value')
print(df_wide.head())
解析:df.pivot() 用于数据透视,将长格式数据转换为宽格式。它要求 (index, columns) 组合是唯一的,否则会报错(此时需用 pivot_table)。
89. 将宽格式的 df_wide 转换回长格式,新的列名为 [‘date’, ‘variable’, ‘value’]。
df_long = df_wide.reset_index().melt(id_vars='date', var_name='variable', value_name='value')
print(df_long.head())
解析:df.melt() 是 pivot 的逆操作,将宽格式“融化”为长格式。id_vars 指定保持不变的列,var_name 和 value_name 为新列命名。
90. 对 DataFrame 应用一个函数,计算每行的 ‘col1’ 和 ‘col2’ 之和,结果存入新列 ‘sum_col’。
df['sum_col'] = df.apply(lambda row: row['col1'] + row['col2'], axis=1)
print(df[['col1', 'col2', 'sum_col']].head())
解析:df.apply(func, axis=1) 将函数应用于每一行。axis=1 是关键,表示按行操作。函数参数 row 是一个 Series,代表该行数据。
91. 使用 eval 方法高效计算新列 ‘result’,其值为 (colA * 2 + colB) / colC。
df.eval('result = (colA * 2 + colB) / colC', inplace=True)
print(df[['colA', 'colB', 'colC', 'result']].head())
解析:df.eval() 使用字符串表达式进行列间运算,通常比 apply 更快,因为它利用了 Pandas 的底层优化。
92. 找出 DataFrame 中 ‘col’ 列最大的前 5 个值的索引。
top5_idx = df['col'].nlargest(5).index
print(list(top5_idx))
解析:Series.nlargest(5) 返回最大的 5 个值及其索引。.index 属性提取这些索引。
93. 将 DataFrame 中所有小于 0 的值替换为 0。
df_clipped = df.clip(lower=0)
print(df_clipped.head())
解析:df.clip(lower, upper) 将小于 lower 的值替换为 lower,大于 upper 的值替换为 upper。这是数据裁剪的便捷方法。
94. 计算 ‘col’ 列的指数加权移动平均(EWMA),跨度(span)为 5。
df['col_ewma'] = df['col'].ewm(span=5, adjust=False).mean()
print(df[['col', 'col_ewma']].head())
解析:Series.ewm() 计算指数加权移动平均,对近期数据赋予更高权重。span 参数与衰减因子相关,adjust=False 使用递推公式。
95. 将 DataFrame 按 ‘group’ 列分组,并对每个分组内的 ‘value’ 列进行标准化(组内Z-score)。
def zscore_within_group(x):
return (x - x.mean()) / x.std()
df['value_group_z'] = df.groupby('group')['value'].transform(zscore_within_group)
print(df[['group', 'value', 'value_group_z']].head())
解析:groupby()[‘col’].transform(func) 对每个分组应用函数,但返回与原始数据形状相同的结果,非常适合组内标准化这类操作。
96. 展开 ‘list_col’ 列(该列每个元素是一个列表),使列表中的每个元素成为独立的一行。
df_exploded = df.explode('list_col')
print(df_exploded.head())
解析:df.explode(column) 将指定列中类似列表的每个元素展开为一行,并复制其他列的值。用于处理嵌套数据。
97. 计算 ‘col’ 列的百分位数:第 25%(Q1)、第 50%(中位数)、第 75%(Q3)。
q1 = df['col'].quantile(0.25)
median = df['col'].quantile(0.50)
q3 = df['col'].quantile(0.75)
print(f"Q1: {q1}, Median: {median}, Q3: {q3}")
解析:Series.quantile(q) 计算分位数,q 介于 0和 1 之间。Q1、中位数、Q3 是描述数据分布和识别异常值的关键。
98. 基于 ‘col’ 列的值,使用 between 方法筛选出值在 10 到 20 之间(包含两端)的所有行。
filtered_between = df[df['col'].between(10, 20, inclusive='both')]
print(filtered_between.head())
解析:Series.between(left, right, inclusive) 返回布尔 Series,表示值是否在指定区间内。inclusive 参数控制是否包含端点。
99. 使用 pd.get_dummies 对 ‘category’ 列进行独热编码(One-Hot Encoding)。
dummies = pd.get_dummies(df['category'], prefix='cat')
df_encoded = pd.concat([df, dummies], axis=1)
print(df_encoded.head())
解析:独热编码将分类变量转换为二进制(0/1)矩阵,是机器学习中处理分类特征的常用方法。prefix 参数为生成的列名添加前缀。
100. 将 DataFrame 导出为 JSON 格式文件 ‘data.json’,采用记录(records)格式。
df.to_json('data.json', orient='records')
解析:df.to_json() 将 DataFrame 导出为 JSON 字符串或文件。orient=‘records’ 格式输出为记录列表,每行是一个 JSON 对象,这是与许多 Web API 交互的常用格式。
参考来源
- 【Python数据分析】Pandas必刷的100道练习题,附答案和讲解
- Pandas 200道练习题:从基础到进阶,轻松掌握数据分析-百度开发者中心
- Pandas数据处理100题详解与实践
- Pandas:让你少走99%弯路的100道python数据分析练习题,大厂必备题!
- 100道Python经典练习题助你夯实基础,附详细解答与学习建议
更多推荐
所有评论(0)