楔子
pandas是一个很强大的库,但是在使用的过程中难免会遇见各种奇葩的异常,而这些异常却又很难让人定位到底是哪一步出了问题。下面就来看看pandas中的一些令人感到费解的异常吧,看看你有没有遇到过,如果没有的话,那么说明你pandas可能用的不够多哦。
ヽ( ̄ω ̄( ̄ω ̄〃)ゝ一起来看看
1. SettingWithCopyWarning:
当然这不是个异常,而是一个警告,这个警告相信大多数人都遇到过,尤其是初学pandas的时候。这个警告具体内容如下:
SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/......
我们来复现一下这个警告:
import pandas as pd
df = pd.DataFrame({"name": ["mashiro", "koishi", "satori", "kurisu"],
"age": [17, 16, 17, 19],
"adult": [None, None, None, None]})
print(df)
"""
name age adult
0 mashiro 17 None
1 koishi 16 None
2 satori 17 None
3 kurisu 19 None
"""
# 我现在想将df中age > 18对应的audit设置为True
# 但是会发现没有效果,并且SettingWithCopyWarning就是由这一行代码引发的
df[df["age"] > 16]["adult"] = True
print(df)
"""
name age adult
0 mashiro 17 None
1 koishi 16 None
2 satori 17 None
3 kurisu 19 None
"""
为什么会出现这个原因呢?因为df[df["age"] > 16]得到的是原始DataFrame的一份拷贝,因此其相应的操作不会影响原来的DataFrame。尽管这样的操作是允许的,但是却无法得到正确的结果,因此pandas弹出了一个警告。
# 真正的做法是使用loc或者iloc
df.loc[df["age"] > 18, "adult"] = True
print(df)
"""
name age adult
0 mashiro 17 None
1 koishi 16 None
2 satori 17 None
3 kurisu 19 None
"""
2. TypeError: ‘Series’ objects are mutable, thus they cannot be hashed
这个异常实际上比较常见了,说白了就是你不小心把loc或者iloc给丢掉了,我们还用上面的例子
try:
df[df["age"] > 18, "adult"] = True
except Exception as e:
print(e) # 'Series' objects are mutable, thus they cannot be hashed
3. ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all()
我们知道,像if a:,not a之类的,本质上都是调用a的__bool__方法,可以理解为bool(a)
class A:
def __bool__(self):
return True
if A():
print("123")
else:
print("456")
print(bool(A()))
"""
123
True
"""
# 由于A的__bool__返回了True, 所以bool(A())为True
# 我们将其改为False
A.__bool__ = lambda self: False
print(bool(A())) # False
但是对于一个Series或者numpy中的array不可以这么做。
import pandas as pd
s = pd.Series([True, False])
try:
bool(s)
except Exception as e:
print(e) # The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
try:
if s.values: # 得到一个numpy中的array
pass
except Exception as e:
print(e) # The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
"""
对于一个Series对象来说,不可以使用其布尔值,应当使用 s.all()、s.any()或者np.all(s)、np.any(s)
但是对于numpy中的array来讲,如果这个array里面的元素不止一个,那么也不可以使用其布尔值,但如果该array中只有一个元素的话是个例外
此时:if np.array([123]) 等价于 if 123
因此如果使用其布尔值的话,最好使用np.all()或者np.any()将一个序列变成单个布尔值
"""
# 但如果只是希望像列表一样,如果该Series对象里面有值就是真,否则就是假
# 那么建议通过 if len(s):这种方式来判断
# 同理DataFrame也是如此
4. 布尔你咋啦?
我们知道,可以对两个类型为bool的Series对象进行 与、或、非 等操作,但是结果真的一定是我们想要的吗?
import pandas as pd
s1 = pd.Series([True, True, True])
s2 = pd.Series([True, False, True], index=[1, 2, 3])
print(s1 & s2)
"""
0 False
1 True
2 False
3 False
dtype: bool
"""
# 我们看到与运算之后,长度变成了4,究其原因就是两个Series索引不同造成的
# 而Series的很多操作都是基于索引进行对齐的,并不是简简单单地按照顺序
# 但如果对应的索引一样的话,那么也可以认为就是按照顺序从上到下
# 但如果索引不一样的话,pandas是怎么做的呢?答案是使用reindex
# 首先找到两个Series对象中出现的所有不重复索引
index = s1.index | s2.index
print(index) # Int64Index([0, 1, 2, 3], dtype='int64')
# 使用reindex进行对齐, 不存在的使用NaN代替,当然我们也可以指定fill_value进行填充
# 比如fill_value=False
print(s1.reindex(index))
"""
0 True
1 True
2 True
3 NaN
dtype: object
"""
print(s2.reindex(index))
"""
0 NaN
1 True
2 False
3 True
dtype: object
"""
# 所以s1 & s2最终等价于 s1.reindex(index) & s2.reindex(index)
# 因此即使两者个数不同也是没有问题的
s1 = pd.Series([True, True, True, True])
s2 = pd.Series([True, True, True], index=[1, 2, 3])
print(s1 & s2)
"""
0 False
1 True
2 True
3 True
dtype: bool
"""
总之pandas中很多操作,并不是我们想的那么简单,pandas的Series和DataFrame都具备索引的概念,通过索引来定位速度是非常快的。但是不注意就会造成陷阱,究其原因就是很多操作在定位的时候是基于索引来定位的,并不是简单的按照顺序。比如:s1 & s2,指的是s1和s2中相同索引对应的元素进行与运算,当然如果有对应不上的,事先已经通过reindex处理好了。
当然,如果我们不希望考虑索引的话,只是单纯的希望按照顺序进行位运算,该怎么做呢?办法有两种
import pandas as pd
s1 = pd.Series([True, True, True])
s2 = pd.Series([True, False, True], index=[1, 2, 3])
# 对Series使用reset_index即可,当然要指定drop=True,否则就变成DataFrame了
# 一旦reset_index之后两者索引从头到尾就是一致的了
print(s1.reset_index(drop=True) & s2.reset_index(drop=True))
"""
0 True
1 False
2 True
dtype: bool
"""
# 或者转成numpy中的array
# 我们知道Series等价于numpy中的n个array,分别存放索引、值等等
# 我们调用s.index即可拿到索引,s.values即可拿到值
print(s1.values & s2)
"""
1 True
2 False
3 True
dtype: bool
"""
# 如果其中是一个array的话,那么它没有索引的概念,索引此时也是单纯的一个一个对应进行运算
# 当然得到的结果也是一个Series,索引和运算的Series的索引保持一致
# 或者都转成array
print(s1.values & s2.values) # [ True False True]
# 但是注意:如果其中一方转成了array,那么此时就要求两个序列的布尔元素个数是必须相等的
# 此时就不会再通过reindex进行扩展了,因为array没有reindex
# 当然都转成array就更不用说了
我们说了很多关于索引的话题,之所以强调这一点,是因为这里面存在一个坑点。我们知道对于DataFrame对象来说,通过df[xxx]可以取得相应的数据,xxx的身份不同,取得的数据也不同
如果xxx是一个标量, 那么df[xxx]表示获取df的某一列,得到一个Series对象
如果xxx是一个列表或者numpy的narray, 那么xxx里面可以是该DataFrame对象的列名,表示获取指定的多个列,得到DataFrame对象
如果xxx是一个列表或者numpy的narray,那么这个xxx里面还可以是布尔值,并且其长度要和该DataFrame对象的行数相等,表示获取对应的行数。对应为True的保留,为False的不要,也是得到DataFrame对象
举个栗子
import pandas as pd
df = pd.DataFrame({"a": [1, 2, 3], "b": [11, 22, 33]})
# 只有第一个为True,因此被保留了下来
print(df[[True, False, False]])
"""
a b
0 1 11
"""
try:
# 但此时指定的4个布尔值
print(df[[True, False, False, False]])
except Exception as e:
print(e) # Item wrong length 4 instead of 3.
"""
告诉我们个数不匹配
所以上面之所以说了索引,就是因为在做运算的的时候可能导致布尔值的个数最终和DataFrame的行数不匹配
从而在筛选指定记录的时候发生报错
"""
如果是Series也是可以的
import pandas as pd
df = pd.DataFrame({"a": [1, 2, 3], "b": [11, 22, 33]})
flag = pd.Series([True, False, False])
print(df[flag])
"""
a b
0 1 11
"""
# 当然里面也可以是一个Series,当然这个Series不仅个数要匹配,索引也要匹配
# 我们知道df的索引是 0 1 2,那么该flag的索引也必须是0 1 2(但是顺序不要求)
flag.index = [1, 2, 3]
try:
df[flag]
except Exception as e:
print(e)
# Unalignable boolean Series provided as indexer (index of the boolean Series and of the indexed object do not match).
"""
我们看到报了上面那个错误,意思我们传递了bool类型的Series对象,但是其索引和DataFrame的索引不匹配
"""
# 我们再改一下
flag.index = [1, 2, 0]
print(flag)
"""
1 True
2 False
0 False
dtype: bool
"""
print(df[flag])
"""
a b
1 2 22
"""
# 我们看到此时布尔值True对应的索引为1,那么筛选的就不再是df中的第一行了
# 而是索引为1的行,也就是第二行。
# 因此尽管对Series的索引的值有要求,但是对顺序却并没有要求
# 所以这种情况下,筛选出来的数据可能就和我们想象的不一样,明明第一个是True,为啥却把DataFrame的第二行选出来了
# 原因就是,虽然第一个是True,但是它对应的索引是1
因此索引这个东西在定位数据的时候,会非常方便,因为我们可以直接通过索引去定位。但是在一些操作方面,我们关心的并不是它的索引,而是它的值,比如:s1 & s2,或者df[flag],这个时候我们只是对内部的布尔值感兴趣,那么直接把s1、s2、flag这些变成numpy中的array之后,再去传递即可。此时就无需考虑索引啥的了。
5. 怎么给DataFrame添加字段呢?
给一个DataFrame添加一个字段,并附上初始值有以下几种方式。
import pandas as pd
df = pd.DataFrame({"a": [1, 2, 3], "b": [11, 22, 33]})
# 可以给一个标量,然后会自动进行广播
df["c"] = "xx"
# 也可以是一个列表,如果里面只有一个元素,那么和标量是等价的
df["d"] = "yy"
print(df)
"""
a b c d
0 1 11 xx yy
1 2 22 xx yy
2 3 33 xx yy
"""
# 如果是列表里面有多个值,那么个数必须和df的行数匹配
# 否则会报出ValueError: Length of values does not match length of index
df["e"] = ["x", "y", "z"]
print(df)
"""
a b c d e
0 1 11 xx yy x
1 2 22 xx yy y
2 3 33 xx yy z
"""
# 还有一种办法是通过df.assign,这种办法可以同时创建多个列
df = df[["a", "b"]]
df = df.assign(
# 这里指定接收一个参数的函数,这个参数就是整个df
# 通过关键字参数,那么参数名就是列名
c=lambda x: x["a"] + 1,
d=lambda x: x["b"] * 2,
e=lambda x: ["i", "j", "k"],
f=lambda x: "哼哼"
)
print(df)
"""
a b c d e f
0 1 11 2 22 i 哼哼
1 2 22 3 44 j 哼哼
2 3 33 4 66 k 哼哼
"""
给一个DataFrame添加一个字段,同样存在索引的陷阱
import pandas as pd
df = pd.DataFrame({"a": [1, 2, 3], "b": [11, 22, 33]})
df["c"] = pd.Series(["x", "y", "z"], index=[0, 2, 3])
print(df)
"""
a b c
0 1 11 x
1 2 22 NaN
2 3 33 y
"""
原因无需我再多解释,总而言之就是我们刚才说的那样,如果我们只关心值,不关心索引,那么就不要传递Series对象,直接传递numpy中的array或者列表即可,这样我们就根本不需要考虑索引对齐的问题。
传递一个一维序列是可以的,那么传递一个DataFrame对象会如何呢?
import pandas as pd
df = pd.DataFrame({"a": [1, 2, 3], "b": [11, 22, 33]})
df1 = pd.DataFrame({"x": ["aa", 22, None], "y": [">>", "^^", "YY"]})
try:
# 因为df1有两个字段,这里我们只指定了一个
df["c"] = df1
except Exception as e:
print(e) # Wrong number of items passed 2, placement implies 1
# 我们知道df1["x"]是个Series,所以df["c"] = df1["x"]肯定没有错
# 但是df["c"] = df1[["x"]]呢? df1[["x"]]显然是个DataFrame
df["c"] = df1[["x"]]
print(df)
"""
a b c
0 1 11 aa
1 2 22 22
2 3 33 None
"""
# 可以看到,如果DataFrame只有一个字段,那么等价于Series
# 最后,df["xx"] = xx 这种方式, 在xx是一维序列的前提下 完全等价于 df.loc[:, "xx"] = xx
# 但如果xx是一个DataFrame的话就不一样了
df = pd.DataFrame({"a": [1, 2, 3], "b": [11, 22, 33]})
df1 = pd.DataFrame({"x": ["aa", 22, None], "y": [">>", "^^", "YY"]})
df.loc[:, "c"] = df1
print(df)
"""
a b c
0 1 11 NaN
1 2 22 NaN
2 3 33 NaN
"""
# 我们惊奇地发现它居然没有报错,但结果却是NaN
# 我们rename一下
df1 = df1.rename(columns={"y": "c"})
df.loc[:, "c"] = df1
print(df)
"""
a b c
0 1 11 >>
1 2 22 ^^
2 3 33 YY
"""
# 因此我们发现在使用df.loc[:, "xx"] = df1的时候
# 会自动去找df1中列名为"xx"的列,如果找不到就为NaN
如果给DataFrame添加多个字段的话,除了assign之外,还有什么办法呢?
import pandas as pd
df = pd.DataFrame({"a": [1, 2, 3], "b": [11, 22, 33]})
df1 = pd.DataFrame({"x": ["aa", 22, None], "y": [">>", "^^", "YY"]})
df[["c", "d"]] = df1
print(df)
"""
a b c d
0 1 11 aa >>
1 2 22 22 ^^
2 3 33 None YY
"""
# 这里可以要求列名不一致,但是个数必须要匹配
df = pd.DataFrame({"a": [1, 2, 3], "b": [11, 22, 33]})
df1 = pd.DataFrame({"x": ["aa", 22, None], "y": [">>", "^^", "YY"]})
# 但是对于loc来说,无法添加多个字段
# 添加一个字段是可以的,但是多个不行
try:
df.loc[:, ["c", "d"]] = df1
except Exception as e:
# loc表示筛选,而df的列中没有"c"和"d"
# 即使是df.loc[:, ["c"]]也不可以,但是df.loc[:, "c"]是可以的
print(e) # "None of [Index(['c', 'd'], dtype='object')] are in the [columns]"
# 所以df[["c", "d"]] = df1,如果列c、d不存在, 那么会自动添加
# 但是对于df.loc[:, ["c", "d"]] = df1,如果c、d不存在,则报错,注意:不是都不存在,而是只要有一个不存在就报错
# 如果是指定了不存在的索引,暂时不会报错,而是弹出一个警告
print(df.loc[[1, 11]])
"""
a b
1 2.0 22.0
11 NaN NaN
"""
# 我们看到指定了不存在的索引,那么自动为NaN
# 但同时会抛出一个FutureWarning:
"""
Passing list-likes to .loc or [] with any missing label will raise
KeyError in the future, you can use .reindex() as an alternative.
"""
# 意思是让我们先reindex一下
所以如果想添加多个字段,可以直接通过df[["c1", "c2"]] = df1的方式,但是注意:右边写的df1,所以右边需要也是一个DataFrame,并且两者列数相等
import pandas as pd
df = pd.DataFrame({"a": [1, 2, 3], "b": [11, 22, 33]})
df1 = pd.DataFrame({"x": ["aa", 22, None], "y": [">>", "^^", "YY"]})
# 这里我们本意是想创建两个字段,值都为None
# 但是很遗憾这样不可以
try:
# 右边的值也需要是一个DataFrame
df[["c", "d"]] = None
except Exception as e:
# 我们看到这里也同样报出了相应的错误
# 因此只有当右边的值是DataFrame的时候,df[["c", "d"]]才能具备创建新字段的能力
print(e) # "None of [Index(['c', 'd'], dtype='object')] are in the [columns]"
# 如果想创建多个新字段,并且还希望通过广播的方式赋上同一个值,那么上面做法是行不通的
# 解决办法是一个字段一个字段的创建,这样百分之百是没有任何问题的,既可以df["c"]也可以df.loc[:, "c"]
# 但是也可以通过我们之前说的assign
df = df.assign(
c=lambda x: None,
d=lambda x: None,
)
print(df)
"""
a b c d
0 1 11 None None
1 2 22 None None
2 3 33 None None
"""
# 除此之外,还有一个insert方法
# 这个方法接收:插入的位置、列名、值
# 比如我想在列c的后面插入一个新列age,值全部是18,该怎么做呢?
df.insert(df.columns.get_loc("c") + 1, "age", 18)
print(df)
"""
a b c age d
0 1 11 None 18 None
1 2 22 None 18 None
2 3 33 None 18 None
"""
# 我们看到insert这个方法是在本地进行操作的
# 关键是第一个参数,我们希望插在c的后面,那么就必须获取c所在的索引,当然也可以直接数出来
# 通过columns.get_loc即可获取,然后再加上1即可
6. ValueError: cannot compute isin with a duplicate axis.
这个错误当初也是把我搞懵逼了半天,在复现这个异常之前,我们先来聊聊非常常用的isin
isin我们一般是对Series对象使用,判断这个序列中每一个元素是不是在另一个序列里面,下面举例说明:
import pandas as pd
s = pd.Series(["a", "b", "c", "d"])
print(s.isin(["a", "c", "e"]))
"""
0 True
1 False
2 True
3 False
dtype: bool
"""
这个方法是我们经常使用的,但是你对DataFrame使用过isin吗?我们有时候需要判断两个序列,看这两个序列中的值是否在另外两个序列里面。
import pandas as pd
df1 = pd.DataFrame({"x": ["a", "b", "c", "d"], "y": [1, 2, 3, 4]})
df2 = pd.DataFrame({"x": ["a", "B", "c", "D"], "y": [1, 2, 4, 3]})
print(df1)
"""
x y
0 a 1
1 b 2
2 c 3
3 d 4
"""
print(df2)
"""
x y
0 a 1
1 B 2
2 c 4
3 D 3
"""
print(df1[["x"]].isin(df2))
# DataFrame中有两列,所以是两列布尔值
"""
x y
0 True True
1 False True
2 True False
3 False False
"""
# 我们来分析一下,对于df1来说,前两行肯定是没有问题的
# 但是第三行有点诡异,我们df1的第三行的y列是3,显然3是在df2的y列当中啊,为什么是False
# 同理第4行,"d"不在df2的x列中我们知道,但是y列的4很明显在df2的y列当中,为什么是False
估计有人猜到了,那就是对DataFrame使用isin的时候,多个列之间并不是独立的。事实上,DataFrame使用isin也是根据索引来的,我们举个栗子
import pandas as pd
df1 = pd.DataFrame({"x": ["a", "b", "c", "d"], "y": [1, 2, 3, 4]})
df2 = pd.DataFrame({"x": ["a", "b", "c", "d"], "y": [1, 2, 3, 4]})
# 两个一模一样的DataFrame对象
print(df1.isin(df2))
"""
x y
0 True True
1 True True
2 True True
3 True True
"""
# 结果没问题,但是我们将df2的索引改变一下
df2.index = [0, 1, 3, 2]
print(df1.isin(df2))
"""
x y
0 True True
1 True True
2 False False
3 False False
"""
# 此时我们就看到端倪了,对于DataFrame对象来讲,isin是判断对应索引的字段的值是否相同
但是问题又来了,因为这样显然不是我们期望的结果。因为即使df2中存在,但如果索引对不上的话也没有任何意义,因此我们可以手动设置索引。
import pandas as pd
df1 = pd.DataFrame({"x": ["a", "b", "c", "d"], "y": [1, 2, 3, 4]})
df2 = pd.DataFrame({"x": ["a", "b", "d", "c"], "y": [1, 2, 4, 3]})
# 我们将x和y设置为索引不就行了,加上drop=False表示设置索引的同时,还作为列
df1 = df1.set_index(["x", "y"], drop=False)
df2 = df2.set_index(["x", "y"], drop=False)
print(df1)
"""
x y
x y
a 1 a 1
b 2 b 2
c 3 c 3
d 4 d 4
"""
print(df2)
"""
x y
x y
a 1 a 1
b 2 b 2
d 4 d 4
c 3 c 3
"""
print(df1.isin(df2))
"""
x y
x y
a 1 True True
b 2 True True
c 3 True True
d 4 True True
"""
# 在通过all(axis=1)即可找到满足条件的值
print(df1.isin(df2).all(axis=1).values) # [ True True True True]
# 我们看到此时根据索引去找,就能够准确的定位了
# 不过细心的人可能已经发现了,这个索引是由x和y两列得到的,事实上索引如果匹配上了,那么值一定是相等的
# 所以此时就没必要在进行对比了
# 是的,所以我们可以换一种方法
df1 = pd.DataFrame({"x": ["a", "b", "c", "d"], "y": [1, 2, 3, 4]})
df2 = pd.DataFrame({"x": ["a", "b", "d", "c"], "y": [1, 2, 4, 3]})
# pandas中有一个Index类,Series和DataFrame的索引就是Index类型
# 当然Index分为好几种,但是它们继承自Index
print(type(df1.index)) # <class 'pandas.core.indexes.range.RangeIndex'>
# 根据x和y两列创建Index对象
index1 = pd.Index(df1[["x", "y"]])
index2 = pd.Index(df2[["x", "y"]])
print(index1) # Index([('a', 1), ('b', 2), ('c', 3), ('d', 4)], dtype='object')
print(index2) # Index([('a', 1), ('b', 2), ('d', 4), ('c', 3)], dtype='object')
# Index对象可以像集合一样,取并集、交集,当然此时我们可以直接使用isin
# 因为它们整体变成了一个元组,也就是说,此时是一个一维序列,对于一维序列可以直接使用isin
# 直接返回一个numpy中的array
print(index1.isin(index2)) # [ True True True True]
然而这么做有一个弊端,没错,我要复现我们上面的异常了
import pandas as pd
df1 = pd.DataFrame({"x": ["a", "b", "c", "d"], "y": [1, 2, 3, 4]})
df2 = pd.DataFrame({"x": ["a", "a", "c", "d"], "y": [1, 1, 3, 4]})
# 我们将x和y设置为索引不就行了,加上drop=False表示设置索引的同时,还作为列
df1 = df1.set_index(["x", "y"], drop=False)
df2 = df2.set_index(["x", "y"], drop=False)
try:
print(df1.isin(df2))
except Exception as e:
print(e) # cannot compute isin with a duplicate axis.
# 我们对一个DataFrame使用isin,那么要求isin里面的DataFrame的索引是不可以重复的,否则就会报出上面这个错误
# 解决办法是使用pd.Index
print(pd.Index(df1[["x", "y"]]).isin(pd.Index(df2[["x", "y"]]))) # [ True False True True]
# 然鹅,我记得pd.Index这种做法也不保险
# 由于索引的特殊性,好像这种情况我记得也报错,但是目前没有
# 因此最稳妥的办法是再转成Series
s1 = pd.Series(pd.Index(df1[["x", "y"]]))
s2 = pd.Series(pd.Index(df2[["x", "y"]]))
print(s1)
"""
0 (a, 1)
1 (b, 2)
2 (c, 3)
3 (d, 4)
dtype: object
"""
print(s2)
"""
0 (a, 1)
1 (a, 1)
2 (c, 3)
3 (d, 4)
dtype: object
"""
print(s1.isin(s2))
"""
0 True
1 False
2 True
3 True
dtype: bool
"""
# 这种做法是百分之百没有问题的
# 忘记说了,df1.isin(df2)的时候,两个列的名称一定要对应
df1 = pd.DataFrame({"x": ["a", "b", "c", "d"], "y": [1, 2, 3, 4]})
df2 = pd.DataFrame({"a": ["a", "a", "c", "d"], "y": [1, 1, 3, 4]})
print(df1.isin(df2))
"""
x y
0 False True
1 False False
2 False True
3 False True
"""
# 由于df2中没有x这一列,因此相当于NaN,所以结果为False
print(df1[["y"]].isin(df2))
"""
y
0 True
1 False
2 True
3 True
"""
# 会自动找df2中名称为y的列进行比较,因此记得注意列名
# 当然由于df1.isin(df2)在索引方面的局限性,我们一般也不会使用这种方法
# 而是会将DataFrame的每一个字段的值拼接成一个元组,整体得到一个Series对象
# 然后对Series对象使用isin,这是最正确的做法
7. ValueError: cannot set a frame with no defined index and a scalar
这个错误不是很常见,我们来看一下。
import pandas as pd
df = pd.DataFrame({"a": [1, 1, 1, 1], "b": [1, 1, 1, 1]})
df.loc[df["a"] > 2, "c"] = 1
print(df)
"""
a b c
0 1 1 NaN
1 1 1 NaN
2 1 1 NaN
3 1 1 NaN
"""
我们将df["a"] > 2的记录选出来,然后同时创建"c"这一列,并设置对应的记录为1。如果不满足条件,那么会自动为NaN,而我们没有满足条件的记录,所以全部为NaN
import pandas as pd
df = pd.DataFrame({"a": [1, 1, 1, 1], "b": [1, 1, 1, 1]})
df.loc[:, "c"] = 1
print(df)
"""
a b c
0 1 1 1
1 1 1 1
2 1 1 1
3 1 1 1
"""
上面这种赋值方式也是可以的,我们之前说,对于一维序列,df["xx"]等价于df.loc[:, "xx"],但实际上还是有点区别的,那就是后者要求DataFrame不可以为空
import pandas as pd
df = pd.DataFrame({"a": [], "b": []})
print(df)
"""
Empty DataFrame
Columns: [a, b]
Index: []
"""
try:
df.loc[:, "c"] = 1
except Exception as e:
print(e) # cannot set a frame with no defined index and a scalar
# 空DataFrame的话,只能用df["c"] = 1的方式
df["c"] = 1
print(df)
"""
Empty DataFrame
Columns: [a, b, c]
Index: []
"""
8. ValueError: If using all scalar values, you must pass an index
这个错误应该遇见的比较少,我们看看这种错误是怎么发生的。
import pandas as pd
# 我们说通过字典构建DataFrame,value应该是序列,不应该是一个标量
try:
df = pd.DataFrame({"a": 123, "b": None})
except Exception as e:
print(e) # If using all scalar values, you must pass an index
# 如果传递标量的话,那么应该同时指定一个index, index是只有一个元素的列表,里面是一个索引
df = pd.DataFrame({"a": 123, "b": None}, index=["索引"])
print(df)
"""
a b
索引 123 None
"""
有待发掘。。。。