复习:数据分析的第一步,加载数据我们已经学习完毕了。当数据展现在我们面前的时候,我们所要做的第一步就是认识他,今天我们要学习的就是了解字段含义以及初步观察数据。
本文引用数据信息,及文章内容来源请先阅读动手学数据分析一
1 第一章:pandas基础
1.4 知道你的数据叫什么——Series 和 DataFrame 初认识
在这一部分,你会学到:
pandas的两个核心数据结构:Series(一维)和DataFrame(二维表格),理解它们分别长什么样、适合装什么数据。- 能自己写出简单示例:
- 用字典创建
Series(如州名 → 数值映射)。- 用字典创建
DataFrame(如 state/year/pop 这样的表格数据)。- 重新加载
train.csv或上一节保存好的train_chinese.csv,并通过df.columns查看每一列的列名。- 学会两种访问列的方式,并理解它们的区别:
- 字典访问法:
df['Cabin']- 属性访问法:
df.Cabin
知道什么时候必须用中括号索引(列名有空格、特殊字符、想用变量名等)。- 学会对比两个 DataFrame(如
train和test_1)的列差异:
- 用集合运算找出“测试集中多出的列”。
- 用多种方式删除多余列:
del、drop(columns=...)、pop()或“只保留需要的列”的思路。- 理解
Unnamed: 0这类列名出现的原因:通常是保存时把索引写进 CSV、读取时又没指定index_col导致的。- 用
df.drop([...], axis=1)实现“临时隐藏几列只看其他列”,并理解:
- 不加
inplace=True时,只是返回一个“修改后的视图/新对象”,原df不会被真正改动。- 何时适合用
inplace=True做“原地修改”。
1.5 筛选的逻辑——用条件把数据“挑”出来
在这一部分,你会学到:
- 利用布尔条件对 DataFrame 做行筛选:
- 例如:
df[df["Age"] < 10]获取 10 岁以下乘客。- 构造多条件筛选,并用逻辑运算符组合条件:
- 使用
&表示“同时满足”(交集),|表示“满足其一”(并集),~表示取反。- 理解为什么每个条件外面都必须加一层括号。
- 完成“10 < Age < 50”的区间筛选,并将结果命名为
midage。- 使用
reset_index()重置筛选后 DataFrame 的索引:
- 理解原索引可能是不连续、不是 0 开始的。
- 明白
reset_index()和reset_index(drop=True)的区别:一个保留旧索引为列,一个直接丢弃。- 知道何时需要先重置索引,再按“行号感觉”的方式去定位行。
- 使用
loc按“标签”选取数据:
- 例如选出
midage中第 100 行指定列("Pclass"、"Sex")的数据。- 以及多行多列组合选择:
loc[[100, 105, 108], ['Pclass', 'Name', 'Sex']]。- 使用
iloc按“位置”选取数据:
- 例如用
iloc[[100,105,108],[2,3,4]]通过“行号 + 列号”的方式选择相同的数据。- 通过对比
loc与iloc,理解它们的关键区别:
loc:按标签(行索引名、列名)选取。iloc:按位置(整数下标)选取。- 切片区间在
loc中是闭区间,在iloc中是左闭右开
1.4 知道你的数据叫什么
我们学习pandas的基础操作,那么上一节通过pandas加载之后的数据,其数据类型是什么呢?
1.4.1 任务一:pandas中有两个数据类型DataFrame和Series,通过查找简单了解他们。然后自己写一个关于这两个数据类型的小例子🌰[开放题]
importnumpyasnpimportpandasaspdsdata={'Ohio':35000,'Texas':71000,'Oregon':16000,'Utah':5000}example_1=pd.Series(sdata)example_1data={'state':['Ohio','Ohio','Ohio','Nevada','Nevada','Nevada'],'year':[2000,2001,2002,2001,2002,2003],'pop':[1.5,1.7,3.6,2.4,2.9,3.2]}example_2=pd.DataFrame(data)example_2- Pandas 主要有两个核心数据结构DataFrame和Series 的介绍
Pandas 主要有两个核心数据结构,以及存储在这些结构中的底层数据类型(dtypes)。
1. 核心数据结构 (Data Structures)
这是 Pandas 中最常用的两个对象:
Series (序列)
- 定义:一维数组对象。
- 理解:可以把它看作是 Excel 表格中的一列,或者是一个带标签(索引)的 Python 列表。
- 组成:由一组数据(Values)和一组与之相关的数据标签(Index)组成。
- 代码示例(参考你的
example_1):# 创建 Seriess=pd.Series([1,3,5,np.nan,6,8])DataFrame (数据框)
定义:二维表格型数据结构。
理解:可以把它看作是整个Excel 表格或 SQL 表。
组成:它既有行索引(Index),也有列索引(Columns)。你可以把它看作是由多个 Series 组成的字典(共用同一个行索引)。
代码示例(参考你的
example_2):# 创建 DataFramedf=pd.DataFrame({'A':1.,'B':pd.Timestamp('20130102'),'C':pd.Series(1,index=list(range(4)),dtype='float32')})2. 底层数据类型 (Dtypes)
这是指存储在 Series 或 DataFrame 单元格中的具体数值类型。你可以通过
df.dtypes查看。常见的包括:
object:通常代表字符串(String),或者是混合类型的数据。int64:整数(Integer)。float64:浮点数(Float),即带小数点的数字。bool:布尔值(True/False)。datetime64:日期时间类型。
1.4.2 任务二:根据上节课的方法载入"train.csv"文件
df=pd.read_csv('./train.csv')df.head(3)也可以加载上一节课保存的"train_chinese.csv"文件。
1.4.3 任务三:查看DataFrame数据的每列的名称
df.columns1.4.4任务四:查看"Cabin"这列的所有值 [有多种方法]
# 字典访问法df['Cabin'].head(3)# 属性访问法 / 点号法df.Cabin.head(3)df['Cabin'].head(3)、 df.Cabin.head(3)这两种方法的区别
这两种方法在当前这个例子中(访问名为 “Cabin” 的列)效果是完全一样的,都返回该列的数据。
但是,它们在适用场景和灵活性上有很大的区别。以下是详细对比:
1.
df['Cabin'](方括号法 / 字典访问法)这是最通用、最推荐的方法。
优点:
支持任意列名:如果列名包含空格(如
"First Name")、特殊符号(如"Price($)")或数字开头,必须用这种方法。支持变量:可以使用变量来动态访问列。
col_name='Cabin'df[col_name]# 可行创建新列:必须用这种方法来添加新列。
df['New_Column']=1# 创建成功避免冲突:如果列名和 Pandas 的内置方法名重名(比如列名叫
count,sum,class),这种方法依然能准确获取列数据。2.
df.Cabin(属性访问法 / 点号法)这是一种语法糖(Syntactic Sugar),主要是为了写起来方便。
优点:
- 书写快捷:少打两个引号和两个括号。
- 代码补全:在 Jupyter Notebook 或 IDE 中,输入
df.后按 Tab 键通常能自动补全列名。缺点(局限性):
- 不支持特殊列名:如果列名有空格(
df.First Name❌)或特殊字符,会报错。- 不能创建新列:
df.New_Column = 1只会给df对象添加一个普通的 Python 属性,而不会在 DataFrame 数据结构中真正添加一列数据。- 容易冲突:如果你的列名叫做
shape、index、T或者count,使用df.shape访问的是 DataFrame 的形状属性,而不是名为 “shape” 的那一列数据。总结建议
- 日常快速查看数据且列名规范(无空格、非关键字)时,可以用
df.ColumnName。- 编写正式脚本、处理复杂列名或创建新列时,请务必使用
df['ColumnName']。
1.4.5 任务五:加载文件"test_1.csv",然后对比"train.csv",看看有哪些多出的列,然后将多出的列删除
手动打印表头(df.columns)进行肉眼对比
经过我们的观察发现一个测试集test_1.csv有一列是多余的,我们需要将这个多余的列删去
test_1=pd.read_csv('test_1.csv')test_1.head(3)# 删除多余的列deltest_1['a']test_1.head(3)【思考】还有其他的删除多余的列的方式吗?
以下是三种删除多余列的方法
为避免和上文方法冲突,这里对三种方法进行了注释,想要测试先注释掉其他删除方法,再重新运行程序。
- 使用 drop() 函数(最推荐)这是最标准的方法,因为它不会像 del 那样直接修改原数据(除非你指定 inplace=True),这在数据处理管道中更安全。
''' #思考回答 # 方法 A:返回一个新的 DataFrame(不改变原数据) test_1_clean = test_1.drop(['a'], axis=1) # 方法 B:直接在原数据上修改(加上 inplace=True) test_1.drop(['a'], axis=1, inplace=True) # 方法 C:使用 columns 参数(更直观,不需要写 axis=1) test_1.drop(columns=['a'], inplace=True) # axis 参数用于指定操作的方向(行还是列)。 # axis=0 (默认):代表 行 (Rows/Index)。也就是纵向 ↓。 # axis=1:代表 列 (Columns)。也就是横向 →。 '''- 使用 pop() 方法
这个方法会删除列,同时把被删除的列作为返回值弹出来。如果你需要把删掉的数据存起来备用,这个方法很有用。
# 删除 'a' 列,并把它的数据赋值给 deleted_col# deleted_col = test_1.pop('a')- 列表切片/筛选(保留法)与其说是“删除”,不如说是“只保留想要的”。当你需要删除的列很多,而保留的列很少时,这种方法最快。
# 只保留 'PassengerId' 和 'Survived',其他的都不要了# test_1 = test_1[['PassengerId', 'Survived']]总结对比
方法 特点 适用场景 del df['col']Python 原生语法,直接修改原数据,无返回值。 快速删除单列,确定不再需要原数据。 df.drop()Pandas 专用,功能强大,支持多列,默认不修改原数据。 最通用,特别是需要删除多列或在链式调用中。 df.pop()删除并返回该列数据。 删除的同时还需要用到这列数据。 df[['col1']]筛选保留。 需要删除的列比保留的列多得多时。
补充:在Pandas 中如何高效、编程化地对比两个 DataFrame 的列差异?
使用 Python 的集合(Set)操作来快速找出不同之处。
方法:使用集合操作 (Set Operations)
集合操作可以让你瞬间找出:
- A 有但 B 没有的列(差集)
- B 有但 A 没有的列(差集)
- 两者共有的列(交集)
- 两者所有的列(并集)
假设你有两个 DataFrame:
train和test_1。# 获取列名集合train_cols=set(df.columns)test_cols=set(test_1.columns)# 1. 找出 test_1 中多出的列 (test_1 有但 train 没有)diff=test_cols-train_colsprint(f"test_1 多出的列:{diff}")# 2. 找出 train 中多出的列 (train 有但 test_1 没有)# diff_2 = train_cols - test_cols# print(f"train 多出的列: {diff_2}")为什么这种方法更高效?
- 自动化:不需要人工逐个单词去核对,避免眼花看错。
- 直接可用:结果是一个集合或列表,你可以直接把这个结果传给
drop()函数来删除多余的列。
这里不显示多余的列是因为 如果按顺序执行该程序 那么不同的列在上方已经被删除了.你需要注释或者删除上方程序后重新运行。
# 找出 test_1 中多余的列extra_columns=set(test_1.columns)-set(df.columns)print(f"多余的列是:{extra_columns}")# 如果想直接删除这些多余的列# test_1.drop(columns=extra_columns, inplace=True)- 输出解释
'Unnamed: 0'。
- 这是 Pandas 读取 CSV 文件时非常常见的一个现象。
现在去观察一下你输入的两个.csv文件,train.csv的第一列是PassengerId,然而train_1.csv的第一列是一个无列名的序号列
所以读取时就会看到一个叫
Unnamed: 0的列。
你可以在 CSV 里给这一列补上列名(比如“序号”),再重新读取一次,对比一下输出,就会更直观地理解这个现象。它的含义是:
这通常是 CSV 文件中原本就存在的索引列(Index Column),但在保存时没有给它起名字,或者读取时没有指定它为索引。详细解释
来源:
当你使用df.to_csv('filename.csv')保存文件时,默认情况下 Pandas 会把 DataFrame 的行索引(0, 1, 2, …)也保存进 CSV 文件,作为第一列。
因为行索引通常没有名字,所以 CSV 文件的第一行(表头)的第一个位置是空的。读取时的表现:
当你再次用pd.read_csv('filename.csv')读取这个文件时,Pandas 发现第一列有数据但表头是空的,它就会自动给这一列起一个名字,叫做Unnamed: 0(意思是:第 0 个没有名字的列)。如何解决/避免?
你有以下几种方法处理它:
方法一:读取时指定索引(推荐)
告诉 Pandas 第一列就是索引,不要把它当成普通数据列读取。# index_col=0 表示把第0列作为行索引test_1=pd.read_csv('test_1.csv',index_col=0)方法二:保存时不保存索引
如果你不需要保存行索引(通常 0,1,2… 这种默认索引是不需要保存的),在保存时加上index=False。# 保存时丢弃索引df.to_csv('filename.csv',index=False)方法三:读取后删除(你现在的做法)
既然已经读进来了,就把它删掉。# 把它加入到要删除的列表中deltest_1['Unnamed: 0']# 或者test_1.drop(columns=['Unnamed: 0'],inplace=True)
1.4.6 任务六: 将[‘PassengerId’,‘Name’,‘Age’,‘Ticket’]这几个列元素隐藏,只观察其他几个列元素
df.drop(['PassengerId','Name','Age','Ticket'],axis=1).head(3)【思考】对比任务五和任务六,是不是使用了不一样的方法(函数),如果使用一样的函数如何完成上面的不同的要求呢?
【思考回答】
如果你希望在原 DataFrame 上永久删除这些列,可以在drop()中使用inplace=True。
本小节只是想“暂时隐藏这几列看一眼效果”,所以不加inplace,让原数据保持不变。
# 思考回答df.head(3)1.5 筛选的逻辑
对于表格数据,最重要的能力之一就是筛选:把自己需要的信息挑出来,把当前分析不关心的先丢在一边。
下面还是用实战的方式,一边写代码一边体会 pandas 的筛选能力。
1.5.1 任务一: 我们以"Age"为筛选条件,显示年龄在10岁以下的乘客信息。
df[df["Age"]<10].head(3)1.5.2 任务二: 以"Age"为条件,将年龄在10岁以上和50岁以下的乘客信息显示出来,并将这个数据命名为midage
midage=df[(df["Age"]>10)&(df["Age"]<50)]midage.head(3)【提示】了解pandas的条件筛选方式以及如何使用交集和并集操作
在 Pandas 中进行条件筛选(Boolean Indexing)时,逻辑运算与 Python 原生的
and/or有所不同。以下是核心规则和操作方式:
1. 核心逻辑符号
Pandas 使用位运算符来处理向量化的逻辑操作:
- 交集 (AND):使用符号
&
- 含义:同时满足所有条件。
- 并集 (OR):使用符号
|(竖线)
- 含义:只要满足其中一个条件即可。
- 取反 (NOT):使用符号
~(波浪号)
- 含义:排除满足该条件的数据。
2. !!!极其重要的语法规则:括号
()这是新手最容易报错的地方:每个独立的条件必须用括号括起来。
错误写法:
df[df['Age']>10&df['Age']<50]# 报错原因:Python 中 & 的优先级比 > 高,# 它会先尝试计算 10 & df['Age'],导致逻辑混乱。正确写法:
df[(df['Age']>10)&(df['Age']<50)]3. 实战示例
假设我们要处理泰坦尼克号的数据:
A. 交集 (AND) - 你的任务二
筛选年龄在 10 岁到 50 岁之间的人(大于10且小于50):
# 两个条件都必须为 Truemidage=df[(df["Age"]>10)&(df["Age"]<50)]B. 并集 (OR)
筛选“小孩”或者“老人”(小于 10 岁或者大于 50 岁):
# 只要满足其中一个条件就保留extreme_age=df[(df["Age"]<10)|(df["Age"]>50)]C. 列表筛选 (isin)
如果你要筛选某个字段等于多个特定值(比如筛选 1 等舱和 3 等舱),用
isin()更简洁,它本质上也是一种并集:# 等同于 (df["Pclass"] == 1) | (df["Pclass"] == 3)df[df["Pclass"].isin([1,3])]
1.5.3 任务三:将midage的数据中第100行的"Pclass"和"Sex"的数据显示出来
midage=midage.reset_index(drop=True)midage.head(3)reset_index(drop=True) 的作用是将 midage 的索引重置为默认的整数索引(0, 1, 2, …),并且不保留原有的索引列(因为 drop=True)。
这样做可以让 midage 的索引变得连续、规范,方便后续按位置(或标签)访问或数据处理,避免因索引混乱导致的错误。
如果不加 drop=True(即写成 midage = midage.reset_index()),原有的索引会被还原为一列,变成 DataFrame 的普通数据列,而不是被直接丢弃。
具体表现为:
- 新的 DataFrame 会多出一列名为 “index”(或原索引名),内容是原来的索引值。
- 索引会被重置为默认的 0, 1, 2, …。
这样做的结果是,原索引信息不会丢失,而是以新的一列保留在数据中。如果你不需要原索引,建议加 drop=True,否则会多出一列。
简要总结:
这行代码让 midage 的索引变为默认的连续数字索引,并且丢弃原来的索引,不再作为新的一列保留。
【思考】这个reset_index()函数的作用是什么?如果不用这个函数,下面的任务会出现什么情况?
reset_index() 函数是 Pandas DataFrame 的一个方法,主要作用是将索引(index)还原为普通列,并生成新的默认整数索引。常见用法有:
- df.reset_index():将当前索引变为普通列,索引重置为默认的 0, 1, 2, …
- df.reset_index(drop=True):将索引重置为默认整数索引,但不保留原索引为列。
如果不用 reset_index(),在某些操作(如 groupby、set_index、过滤等)后,DataFrame 的索引可能不是默认的整数索引,而是某些列的值或多级索引。这样会导致:
- 数据显示时索引不是连续数字,可能影响可读性。
- 后续按位置(如 iloc)或合并、可视化等操作时,索引不规范可能导致报错或结果异常。
- 某些情况下,索引列不会参与普通的列运算或输出,容易遗漏。
举例说明:
假设 groupby 后:
df2=df.groupby('A').sum()print(df2)此时 ‘A’ 变成了索引。如果你想让 ‘A’ 重新变成普通列,可以用:
df2=df2.reset_index()如果不 reset_index,后续用 df2[‘A’] 会报错,因为 ‘A’ 只是索引,不是普通列。
总结:reset_index() 让索引变回普通列,保证数据结构统一,便于后续处理。如果不用,可能导致数据操作和结果出现问题。
# 将midage的数据中第100行的"Pclass"和"Sex"的数据显示出来midage.loc[[100],['Pclass','Sex']]1.5.4 任务四:使用loc方法将midage的数据中第100,105,108行的"Pclass","Name"和"Sex"的数据显示出来
midage.loc[[100,105,108],['Pclass','Name','Sex']]1.5.5 任务五:使用iloc方法将midage的数据中第100,105,108行的"Pclass","Name"和"Sex"的数据显示出来
midage.iloc[[100,105,108],[2,3,4]]【思考】对比iloc和loc的异同
iloc和loc都是 Pandas 中用于选取 DataFrame 或 Series 数据的索引器相同点:
- 都可以用于行、列的选取和切片。
- 都支持单个、多个、切片、布尔数组等多种索引方式。
不同点:
iloc(integer location) loc(label location) 索引方式 只能用整数位置(0, 1, 2, …) 用标签(行/列名) 包含性 切片时,结尾不包含(左闭右开) 切片时,结尾包含(左闭右闭) 典型用法 df.iloc[0:3, 1:3] df.loc[‘a’:‘c’, ‘col1’:‘col3’] 错误类型 超出范围时报 IndexError 标签不存在时报 KeyError 举例:
# iloc 按位置df.iloc[0:2,1:3]# 选第0、1行,第1、2列# loc 按标签df.loc['row1':'row3','A':'C']# 选标签从row1到row3,列A到C(都包含结尾)总结:
- iloc 用于“按位置”取数据,适合数字索引。
- loc 用于“按标签”取数据,适合行名、列名索引。