------
初学者经常分不清何时用df[]、df.loc[]和df.iloc[],以及要不要先把检索词用[]括起来做成列表再传参,看完本文保准帮您彻底搞明白。
省流:请直接看 6 小结。
以以下df为例。
data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
columns = ["A", "B", "C"]
df = pd.DataFrame(data=data, columns=columns)
"""
A B C
0 1 2 3
1 4 5 6
2 7 8 9
"""
目录
在df[]、df.loc[]和df.iloc[]这3个可选项中,如果同时抽行列,就只能用df.loc[]或df.iloc[],df[]情况稍复杂。
1 抽列
1.1 抽1列(1),返回 Series 或 DataFrame,一维或二维
kw = "A"
# ------标签索引
s = df[kw]
# ------或直接写:s = df["A"]
以上抽出“A”列。
当然也可直接写s = df[“A”],分开写是为加深印象,可直接传入1个字符串取出对应列。结果如下:
0 1
1 4
2 7
Name: A, dtype: int64
结果为“<class ‘pandas.core.series.Series’>”,一维。
可以传[“A”]吗?
kw = ["A"]
# ------标签索引
s = df[kw]
# 或直接写:s = df[["A"]]
A
0 1
1 4
2 7
可以,返回类型为“<class ‘pandas.core.frame.DataFrame’>”,二维。
请注意:df[]直接传入1个文本串的便利给了列了,抽行就不允许直接给df[]传入1个文本串了,哪怕行的索引名称与列的索引名称不同。在下述“抽行”中再详讲。
用df.loc[]如何抽出“A”列?
s = df.loc[:, "A"]
# ------标签索引,得到Series
df1 = df.loc[:, ["A"]]
# ------标签索引,得到DataFrame
同理如果用df.iloc[],这样写:
s = df.iloc[:, 0]
# ------位置索引,得到Series
df1 = df.iloc[:, [0]]
# ------位置索引,得到DataFrame
用到了半角冒号即切片,牵涉到“抽行列”了,在下述“3 抽行列”部分会具体再讲。
小小结:(1)df[“A”]或df.loc[:, “A”]或df.iloc[:, 0]:得到Series;(2)df[[“A”]]或df.loc[:, [“A”]]或df.iloc[:, [0]]:得到DataFrame;(3)使用标签索引或位置索引。
s = df["A"]
s = df.loc[:, "A"]
s = df.iloc[:, 0]
"""
以上得到Series
"""
df1 = df[["A"]]
df1 = df.loc[:, ["A"]]
df1 = df.iloc[:, [0]]
"""
以上得到DataFrame
"""
1.2 抽指定单独多列(单多),返回 DataFrame
可以一口气指定两列吗,比如“A”和“C”?返回值不可能有Series了,只能是DataFrame。
kw = "A", "C"
# ------报错!
df1 = df[kw]
不可,报错,KeyError: (‘A’, ‘C’)。很明显kw是个元组。没加括号啊,怎么就成元组了?别忘了元组的灵魂是逗号不是括号。df[]接受的参数,可以是单一文本串(如1.1所述),也可以是多个文本串,前提是放进列表,不能是元组。
kw = ["A", "C"]
# ------标签索引。单写"A", "C"的话是个元组
df1 = df[kw]
"""
A C
0 1 3
1 4 6
2 7 9
"""
df.loc[]的写法为:
df1 = df.loc[:, ["A", "C"]]
# ------还是标签索引,但用 .loc 就得放在 , 后边,因为 .loc 的标准写法是 .loc[行, 列]
# ------只要列,就是全要行
df.iloc的写法为:
df1 = df.iloc[:, [0, 2]]
用到了半角冒号即切片,牵涉到“抽行列”了,在下述“3 抽行列”部分会具体再讲。
小小结:
(1)df[[“A”, “C”]]或df.loc[:, [“A”, “C”]]或df.iloc[:, [0, 2]]:得到DataFrame;(2)使用标签索引或位置索引,都要写入列表使用。
df1 = df[["A", "C"]]
df1 = df.loc[:, ["A", "C"]]
df1 = df.iloc[:, [0, 2]]
"""
以上都得到DataFrame
"""
1.3 抽连续多列(连多)
能不能直接写df[“A”:“C”]呢?不能,因为这个便利给了行。也是为了平衡嘛,既然df[]传1个文本串(列名)的便利给了列,也得给行个便利维持平衡嘛,不然行要有意见了嘛!抽连续多列需用半角冒号,必须用df.loc[]或df.iloc[],此处不提,放“抽行列”中说。
2 抽行
2.1 抽1行(1)
前边说了,df[]如果只传入1个文本串,这个便利给了列,抽行不能这样写了,应该怎样写呢,一会儿说。
先看用df.loc[]怎么抽。
kw = 0
# ------位置索引
s = df.loc[kw]
# ------或直接写:s = df.loc[0]
"""
A 1
B 2
C 3
Name: 0, dtype: int64
"""
默认行索引是0、1、2,也可以改成文本:
df.index=["第一行", "第二行", "第三行"]
"""
A B C
第一行 1 2 3
第二行 4 5 6
第三行 7 8 9
"""
这时候可以指定文本取行:
kw = "第一行"
# ------标签索引
s = df.loc[kw]
# ------或直接写:s = df.loc["第一行"]
"""
A 1
B 2
C 3
Name: 第一行, dtype: int64
"""
当然也可以传入列表,得到DataFrame类型结果:
kw = ["第一行"]
df1 = df.loc[kw]
# ------或直接写:df1 = df.loc[["第一行"]]
"""
A B C
第一行 1 2 3
"""
要用df.iloc[]怎么写呢?比如还是抽“第一行”,这样写:
s = df.iloc[0]
# ------位置索引。得到Series
df1 = df.iloc[0:1]
# ------位置索引。得到DataFrame
df1 = df.iloc[:1]
# ------位置索引。得到DataFrame
要用df[],怎么写呢?前边说了不能传1个文本串,怎么办?我们可以传2个文本串:
df1 = df["第一行":"第一行"]
# ------标签索引
好玩吧!更好玩的是,虽然我们已经把行索引改名为“第一行”“第二行”等等了,但我们仍然可以这样取“第一行”:
df1 = df[0:1] # 或df[:1],等于df.iloc[0:1]或df.iloc[:1]
# ------位置索引
这里用到了数字的行编号(也从0开始,其实不就是数字索引?),但写的时候却不是用的df.iloc[]。
写df[0]可以吗?不行,报错。
是不是因为现在的行索引是“第一行”“第二行”这样的,所以写df[0]不行?要是改回默认索引呢?
df.index = [0, 1, 2, 3, 4, 5, 6]
# df1 = df[0] # 仍然报错!
df1 = df.loc[0] # 可以
就想写df[0],还不报错,怎么办?
df.columns = [0, "B", "C"]
df1 = df[0]
这样确实可以,但是这样取的是列,不是行。如果觉得有点绕,建议把上边的内容再多看几遍。
小小结:
(1)df.loc[“第一行”]:得到Series;(2)df[“第一行”:“第一行”]或df[0:1]或df[:1]或df.loc[[“第一行”]]或df.iloc[0:1]或df.iloc[:1]:得到DataFrame。
s = df.loc["第一行"]
s = df.iloc[0]
"""
以上得到Series
"""
df1 = df["第一行":"第一行"]
df1 = df[0:1]
df1 = df[:1]
df1 = df.loc[["第一行"]]
df1 = df.iloc[0:1]
df1 = df.iloc[:1]
"""
以上得到DataFrame
"""
2.2 抽指定单独多行(单多)
没有找到可以直接用df[]、传入多个字符串列表的写法,应该是被列独占了(如说的不对请在评论区留言指教,多谢!)。
返回结果不可能是Series了,只能是DataFrame了。
用df.loc[],指定多个文本串,仍需放入列表:
kw = ["第一行", "第三行"]
df1 = df.loc[kw]
"""注意把第1列的列名0又换回“A”了
A B C
第一行 1 2 3
第三行 7 8 9
"""
df.iloc的写法:
df.iloc[[0, 2]]
小小结:
df.loc[[“第一行”, “第三行”]]或df.iloc[[0, 2]],得到DataFrame。
df1 = df.loc[["第一行", "第三行"]]
df1 = df.iloc[[0, 2]]
"""
以上得到DataFrame
"""
2.3 抽连续多行(连多)
3行数据不过瘾哈,咱们先给df再加4行,方便举例:
df2 = pd.DataFrame(
{
"A":[11, 12, 13, 14],
"B":[21, 22, 23, 24],
"C":[31, 32, 33, 34]
},
index=["第四行", "第五行", "第六行", "第七行"])
df = df.append(df2)
"""
A B C
第一行 1 2 3
第二行 4 5 6
第三行 7 8 9
第四行 11 21 31
第五行 12 22 32
第六行 13 23 33
第七行 14 24 34
"""
多加几行的做法就是给原来的df再append1个同构的DataFrame。
我们连续取“第二行”到“第五行”,如2.1节所示,可以用df[“”:“”]:
df1 = df["第二行":"第五行"]
"""
A B C
第二行 4 5 6
第三行 7 8 9
第四行 11 21 31
第五行 12 22 32
"""
df.loc[]此时与df[]用法一样:
df1 = df.loc["第二行":"第五行"]
"""
A B C
第二行 4 5 6
第三行 7 8 9
第四行 11 21 31
第五行 12 22 32
"""
请注意:此处冒号与列表索引切片的意义稍有不同,列表索引切片是左闭右开,此处指定结尾“第五行”是包括“第五行”的。
如果要取“第一行”到“第五行”,直接写df1 = df.loc[:“第五行”]也可以。
大家想想如果用索引,也就是df.iloc怎么取“第二行”到“第五行”?
物理行25,索引行就是14(因为索引是从0开始),索引写14还不行,因为切片是左闭右开,想包括4就得写5,连起来就是“1:5”:
df1 = df.iloc[1:5]
"""
A B C
第二行 4 5 6
第三行 7 8 9
第四行 11 21 31
第五行 12 22 32
"""
用df[]时指定行编号也可以:
df1 = df[1:5] # 等同于df.iloc[1:5],注意此处df[]也是左闭右开!
"""
A B C
第二行 4 5 6
第三行 7 8 9
第四行 11 21 31
第五行 12 22 32
"""
小小结:
(1)df[“第二行”:“第五行”](左闭右闭)或df[1:5](左闭右开);(2)df.loc[“第二行”:“第五行”](左闭右闭)或df.iloc[1:5](左闭右开)。
df1 = df["第二行":"第五行"] # 左闭右闭
df1 = df[1:5] # 左闭右开
df1 = df.loc["第二行":"第五行"] # 左闭右闭
df1 = df.iloc[1:5] # 左闭右开
3 抽行列(df.(i)loc[1或单多或连多, 1或单多或连多])
抽行列必须用df.loc[]或df.iloc[]。
行与列之间要加个半角逗号,最基础原型为:
df1 = df.loc[:,:]
半角逗号是灵魂。
直接上例子,搞定抽行、抽列,抽行列无非就多了个半角逗号:
df1 = df.loc["第一行", "A"] # 行名为“第一行”和列名为“A”的交叉点,具体值
df2 = df.loc[["第一行"], ["A"]]
"""还是行名为“第一行”和列名为“A”的交叉点,但这次传入的是列表,结果是二维DataFrame
A
第一行 1
"""
df3 = df.loc[["第一行", "第四行"], ["A", "C"]]
"""df3抽提数据的本质做法和df2其实一样一样的
A C
第一行 1 3
第四行 11 31
"""
df4 = df.loc[:, :] # 全行全列都抽提
# 3列操作起来不过瘾,再加1列
s = [16, 26, 36, 46, 56, 66, 76]
df.insert(3, "D", s) # 在列索引为3处插入1列
"""
A B C D
第一行 1 2 3 16
第二行 4 5 6 26
第三行 7 8 9 36
第四行 11 21 31 46
第五行 12 22 32 56
第六行 13 23 33 66
第七行 14 24 34 76
"""
df5 = df.loc["第二行":"第五行", "A":"C"]
"""
A B C
第二行 4 5 6
第三行 7 8 9
第四行 11 21 31
第五行 12 22 32
"""
所以,1.2小节中df1 = df[[“A”, “C”]]其实等同于df1 = df.loc[:, [“A”, “C”]],也就是取全部行,指定单独两列。
所以,哪怕单独取行或列也直接先写个df.loc[,],再在逗号前后加内容即可,逗号前是行,后是列。如果想取具体值或一维Series类型,就传入单个文本串;想取二维DataFrame类型,就传入列表或切片(“:”),省得写错。等理解力达到一定水准,再用其它灵活的写法不迟。
4 df.iat[]
把它单列,如果需要读写某行与列交叉点具体数据,非常方便。
this = df.iat[0, 1] # 2
索引01,物理12,即取第1行与第2列的交叉值。
注意:不能写df.iat(0, 1)。df后如果紧跟一个括号,只能是[],从来没有(),Series可以跟()。
等同于:
this = df.iloc[0, 1]
this = df.loc["第一行", "B"]
df.iat[, ]只接收行索引与列索引两个数字,不像loc和iloc可以接收那么多写法。df.iat[]只取到1个具体点,用起来不累。
5 按条件抽
一提条件,那肯定跟bool的真假有关喽。
先把之前的df改改,方便说事儿:
df.index = ["张三", "李四", "王五", "刘六", "傻根", "许三多", "钟馗"]
df.columns = ["语文", "数学", "英语", "生物"]
"""
语文 数学 英语 生物
张三 1 2 3 16
李四 4 5 6 26
王五 7 8 9 36
刘六 11 21 31 46
傻根 12 22 32 56
许三多 13 23 33 66
钟馗 14 24 34 76
"""
考得都很一般啊,瘸子里挑将军呗,找找英语考过20分的:
tfilter = df["英语"] > 20
print(tfilter)
"""
张三 False
李四 False
王五 False
刘六 True
傻根 True
许三多 True
钟馗 True
Name: 英语, dtype: bool
"""
看到吧,这个条件返回的是个逻辑型的一维Series数据。应用一下条件:
df1 = df[filter]
print(df1)
"""
语文 数学 英语 生物
刘六 11 21 31 46
傻根 12 22 32 56
许三多 13 23 33 66
钟馗 14 24 34 76
"""
要是取英语、数学都考过20分的呢?
# condition = df["英语"] > 20 & df["数学"] > 20 # 报错,用and也不对
# condition = (df["英语"] > 20 & df["数学"] > 20) # 报错
condition = (df["英语"] > 20) & (df["数学"] > 20)
print(condition)
"""
张三 False
李四 False
王五 False
刘六 True
傻根 True
许三多 True
钟馗 True
dtype: bool
"""
应用条件,df1 = df[condition]即可。要是或呢?把“&”换成“|”即可。当然写熟了直接写df1 = df[(df[“英语”] > 20) & (df[“数学”] > 20)]即可,但是这样写对新手(比如我)来说,不是很友好,又是方括号又是圆括号,容易迷瞪。多写一行又不会死,对吧?
df[“数学”]写成df.数学也可以。其实吧,有时候可选项多反而更让人痛苦。我是安安静静光靠脸当帅哥也能吃饱吃好好呢,还是再学点新东西以个人素养取胜?
条件的书写格式为:单独的条件用括号括起来,再做逻辑运算。逻辑与要写&,不能写and,同理不能写or要写|。问一句:not怎么写?
小小结:
(1)单条件:
先做过滤器:
filter = df[“A”] > 20;
再应用:
df1 = df[filter]。
(2)多条件:
先做各过滤器:
filter1 = df[“A”] > 20;
filter2 = df[“B”] < 5;
filter3 = …
再做过滤器逻辑运算:
filter = filter1 & filter2 not filter3 or …
最后应用:
df1 = df[filter]。
6 小结
是不是有点蒙了?用法太多,实在不容易记住。
简单一点儿。
实战只需记住df.loc[,]!省得分不清什么时候用df[]什么时候用df.loc[],我都用df.loc[,]就是。
应试就得全面掌握。
写法上可能最复杂,但记起来却最简单。
不管敌人来什么招,我都给他回个“亢龙有悔”。
个性的不好记,就先记共性的,等熟练了再说。学习还不就是这样,一口气吃不成胖子。
以上详细讲了DataFrame的“查”,稍微提了“增”,其实只要能“查到”,“删”和“改”就和玩儿一样了。
Pandas 用户文档最全面。如有错误,欢迎批评!
推荐免费爬虫解决方案:https://affiliate.bazhuayu.com/M8lKUC