pandas中的那些让人有点懵逼的异常(坑向)
楔子
pandas是一個(gè)很強(qiáng)大的庫(kù),但是在使用的過程中難免會(huì)遇見各種奇葩的異常,而這些異常卻又很難讓人定位到底是哪一步出了問題。下面就來看看pandas中的一些令人感到費(fèi)解的異常吧,看看你有沒有遇到過,如果沒有的話,那么說明你pandas可能用的不夠多哦。
ヽ( ̄ω ̄( ̄ω ̄〃)ゝ一起來看看
1. SettingWithCopyWarning:
當(dāng)然這不是個(gè)異常,而是一個(gè)警告,這個(gè)警告相信大多數(shù)人都遇到過,尤其是初學(xué)pandas的時(shí)候。這個(gè)警告具體內(nèi)容如下:
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/......
我們來復(fù)現(xiàn)一下這個(gè)警告:
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
"""
# 我現(xiàn)在想將df中age > 18對(duì)應(yīng)的audit設(shè)置為True
# 但是會(huì)發(fā)現(xiàn)沒有效果,并且SettingWithCopyWarning就是由這一行代碼引發(fā)的
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
"""
為什么會(huì)出現(xiàn)這個(gè)原因呢?因?yàn)閐f[df["age"] > 16]得到的是原始DataFrame的一份拷貝,因此其相應(yīng)的操作不會(huì)影響原來的DataFrame。盡管這樣的操作是允許的,但是卻無法得到正確的結(jié)果,因此pandas彈出了一個(gè)警告。
# 真正的做法是使用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
這個(gè)異常實(shí)際上比較常見了,說白了就是你不小心把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之類的,本質(zhì)上都是調(diào)用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
但是對(duì)于一個(gè)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: # 得到一個(gè)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()
"""
對(duì)于一個(gè)Series對(duì)象來說,不可以使用其布爾值,應(yīng)當(dāng)使用 s.all()、s.any()或者np.all(s)、np.any(s)
但是對(duì)于numpy中的array來講,如果這個(gè)array里面的元素不止一個(gè),那么也不可以使用其布爾值,但如果該array中只有一個(gè)元素的話是個(gè)例外
此時(shí):if np.array([123]) 等價(jià)于 if 123
因此如果使用其布爾值的話,最好使用np.all()或者np.any()將一個(gè)序列變成單個(gè)布爾值
"""
# 但如果只是希望像列表一樣,如果該Series對(duì)象里面有值就是真,否則就是假
# 那么建議通過 if len(s):這種方式來判斷
# 同理DataFrame也是如此
4. 布爾你咋啦?
我們知道,可以對(duì)兩個(gè)類型為bool的Series對(duì)象進(jìn)行 與、或、非 等操作,但是結(jié)果真的一定是我們想要的嗎?
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
"""
# 我們看到與運(yùn)算之后,長(zhǎng)度變成了4,究其原因就是兩個(gè)Series索引不同造成的
# 而Series的很多操作都是基于索引進(jìn)行對(duì)齊的,并不是簡(jiǎn)簡(jiǎn)單單地按照順序
# 但如果對(duì)應(yīng)的索引一樣的話,那么也可以認(rèn)為就是按照順序從上到下
# 但如果索引不一樣的話,pandas是怎么做的呢?答案是使用reindex
# 首先找到兩個(gè)Series對(duì)象中出現(xiàn)的所有不重復(fù)索引
index = s1.index | s2.index
print(index) # Int64Index([0, 1, 2, 3], dtype='int64')
# 使用reindex進(jìn)行對(duì)齊, 不存在的使用NaN代替,當(dāng)然我們也可以指定fill_value進(jìn)行填充
# 比如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最終等價(jià)于 s1.reindex(index) & s2.reindex(index)
# 因此即使兩者個(gè)數(shù)不同也是沒有問題的
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中很多操作,并不是我們想的那么簡(jiǎn)單,pandas的Series和DataFrame都具備索引的概念,通過索引來定位速度是非常快的。但是不注意就會(huì)造成陷阱,究其原因就是很多操作在定位的時(shí)候是基于索引來定位的,并不是簡(jiǎn)單的按照順序。比如:s1 & s2,指的是s1和s2中相同索引對(duì)應(yīng)的元素進(jìn)行與運(yùn)算,當(dāng)然如果有對(duì)應(yīng)不上的,事先已經(jīng)通過reindex處理好了。
當(dāng)然,如果我們不希望考慮索引的話,只是單純的希望按照順序進(jìn)行位運(yùn)算,該怎么做呢?辦法有兩種
import pandas as pd
s1 = pd.Series([True, True, True])
s2 = pd.Series([True, False, True], index=[1, 2, 3])
# 對(duì)Series使用reset_index即可,當(dāng)然要指定drop=True,否則就變成DataFrame了
# 一旦reset_index之后兩者索引從頭到尾就是一致的了
print(s1.reset_index(drop=True) & s2.reset_index(drop=True))
"""
0 True
1 False
2 True
dtype: bool
"""
# 或者轉(zhuǎn)成numpy中的array
# 我們知道Series等價(jià)于numpy中的n個(gè)array,分別存放索引、值等等
# 我們調(diào)用s.index即可拿到索引,s.values即可拿到值
print(s1.values & s2)
"""
1 True
2 False
3 True
dtype: bool
"""
# 如果其中是一個(gè)array的話,那么它沒有索引的概念,索引此時(shí)也是單純的一個(gè)一個(gè)對(duì)應(yīng)進(jìn)行運(yùn)算
# 當(dāng)然得到的結(jié)果也是一個(gè)Series,索引和運(yùn)算的Series的索引保持一致
# 或者都轉(zhuǎn)成array
print(s1.values & s2.values) # [ True False True]
# 但是注意:如果其中一方轉(zhuǎn)成了array,那么此時(shí)就要求兩個(gè)序列的布爾元素個(gè)數(shù)是必須相等的
# 此時(shí)就不會(huì)再通過reindex進(jìn)行擴(kuò)展了,因?yàn)閍rray沒有reindex
# 當(dāng)然都轉(zhuǎn)成array就更不用說了
我們說了很多關(guān)于索引的話題,之所以強(qiáng)調(diào)這一點(diǎn),是因?yàn)檫@里面存在一個(gè)坑點(diǎn)。我們知道對(duì)于DataFrame對(duì)象來說,通過df[xxx]可以取得相應(yīng)的數(shù)據(jù),xxx的身份不同,取得的數(shù)據(jù)也不同
如果xxx是一個(gè)標(biāo)量, 那么df[xxx]表示獲取df的某一列,得到一個(gè)Series對(duì)象
如果xxx是一個(gè)列表或者numpy的narray, 那么xxx里面可以是該DataFrame對(duì)象的列名,表示獲取指定的多個(gè)列,得到DataFrame對(duì)象
如果xxx是一個(gè)列表或者numpy的narray,那么這個(gè)xxx里面還可以是布爾值,并且其長(zhǎng)度要和該DataFrame對(duì)象的行數(shù)相等,表示獲取對(duì)應(yīng)的行數(shù)。對(duì)應(yīng)為True的保留,為False的不要,也是得到DataFrame對(duì)象
舉個(gè)栗子
import pandas as pd
df = pd.DataFrame({"a": [1, 2, 3], "b": [11, 22, 33]})
# 只有第一個(gè)為True,因此被保留了下來
print(df[[True, False, False]])
"""
a b
0 1 11
"""
try:
# 但此時(shí)指定的4個(gè)布爾值
print(df[[True, False, False, False]])
except Exception as e:
print(e) # Item wrong length 4 instead of 3.
"""
告訴我們個(gè)數(shù)不匹配
所以上面之所以說了索引,就是因?yàn)樵谧鲞\(yùn)算的的時(shí)候可能導(dǎo)致布爾值的個(gè)數(shù)最終和DataFrame的行數(shù)不匹配
從而在篩選指定記錄的時(shí)候發(fā)生報(bào)錯(cuò)
"""
如果是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
"""
# 當(dāng)然里面也可以是一個(gè)Series,當(dāng)然這個(gè)Series不僅個(gè)數(shù)要匹配,索引也要匹配
# 我們知道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).
"""
我們看到報(bào)了上面那個(gè)錯(cuò)誤,意思我們傳遞了bool類型的Series對(duì)象,但是其索引和DataFrame的索引不匹配
"""
# 我們?cè)俑囊幌?flag.index = [1, 2, 0]
print(flag)
"""
1 True
2 False
0 False
dtype: bool
"""
print(df[flag])
"""
a b
1 2 22
"""
# 我們看到此時(shí)布爾值True對(duì)應(yīng)的索引為1,那么篩選的就不再是df中的第一行了
# 而是索引為1的行,也就是第二行。
# 因此盡管對(duì)Series的索引的值有要求,但是對(duì)順序卻并沒有要求
# 所以這種情況下,篩選出來的數(shù)據(jù)可能就和我們想象的不一樣,明明第一個(gè)是True,為啥卻把DataFrame的第二行選出來了
# 原因就是,雖然第一個(gè)是True,但是它對(duì)應(yīng)的索引是1
因此索引這個(gè)東西在定位數(shù)據(jù)的時(shí)候,會(huì)非常方便,因?yàn)槲覀兛梢灾苯油ㄟ^索引去定位。但是在一些操作方面,我們關(guān)心的并不是它的索引,而是它的值,比如:s1 & s2,或者df[flag],這個(gè)時(shí)候我們只是對(duì)內(nèi)部的布爾值感興趣,那么直接把s1、s2、flag這些變成numpy中的array之后,再去傳遞即可。此時(shí)就無需考慮索引啥的了。
5. 怎么給DataFrame添加字段呢?
給一個(gè)DataFrame添加一個(gè)字段,并附上初始值有以下幾種方式。
import pandas as pd
df = pd.DataFrame({"a": [1, 2, 3], "b": [11, 22, 33]})
# 可以給一個(gè)標(biāo)量,然后會(huì)自動(dòng)進(jìn)行廣播
df["c"] = "xx"
# 也可以是一個(gè)列表,如果里面只有一個(gè)元素,那么和標(biāo)量是等價(jià)的
df["d"] = "yy"
print(df)
"""
a b c d
0 1 11 xx yy
1 2 22 xx yy
2 3 33 xx yy
"""
# 如果是列表里面有多個(gè)值,那么個(gè)數(shù)必須和df的行數(shù)匹配
# 否則會(huì)報(bào)出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,這種辦法可以同時(shí)創(chuàng)建多個(gè)列
df = df[["a", "b"]]
df = df.assign(
# 這里指定接收一個(gè)參數(shù)的函數(shù),這個(gè)參數(shù)就是整個(gè)df
# 通過關(guān)鍵字參數(shù),那么參數(shù)名就是列名
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 哼哼
"""
給一個(gè)DataFrame添加一個(gè)字段,同樣存在索引的陷阱
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
"""
原因無需我再多解釋,總而言之就是我們剛才說的那樣,如果我們只關(guān)心值,不關(guān)心索引,那么就不要傳遞Series對(duì)象,直接傳遞numpy中的array或者列表即可,這樣我們就根本不需要考慮索引對(duì)齊的問題。
傳遞一個(gè)一維序列是可以的,那么傳遞一個(gè)DataFrame對(duì)象會(huì)如何呢?
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:
# 因?yàn)閐f1有兩個(gè)字段,這里我們只指定了一個(gè)
df["c"] = df1
except Exception as e:
print(e) # Wrong number of items passed 2, placement implies 1
# 我們知道df1["x"]是個(gè)Series,所以df["c"] = df1["x"]肯定沒有錯(cuò)
# 但是df["c"] = df1[["x"]]呢? df1[["x"]]顯然是個(gè)DataFrame
df["c"] = df1[["x"]]
print(df)
"""
a b c
0 1 11 aa
1 2 22 22
2 3 33 None
"""
# 可以看到,如果DataFrame只有一個(gè)字段,那么等價(jià)于Series
# 最后,df["xx"] = xx 這種方式, 在xx是一維序列的前提下 完全等價(jià)于 df.loc[:, "xx"] = xx
# 但如果xx是一個(gè)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
"""
# 我們驚奇地發(fā)現(xiàn)它居然沒有報(bào)錯(cuò),但結(jié)果卻是NaN
# 我們r(jià)ename一下
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
"""
# 因此我們發(fā)現(xiàn)在使用df.loc[:, "xx"] = df1的時(shí)候
# 會(huì)自動(dòng)去找df1中列名為"xx"的列,如果找不到就為NaN
如果給DataFrame添加多個(gè)字段的話,除了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
"""
# 這里可以要求列名不一致,但是個(gè)數(shù)必須要匹配
df = pd.DataFrame({"a": [1, 2, 3], "b": [11, 22, 33]})
df1 = pd.DataFrame({"x": ["aa", 22, None], "y": [">>", "^^", "YY"]})
# 但是對(duì)于loc來說,無法添加多個(gè)字段
# 添加一個(gè)字段是可以的,但是多個(gè)不行
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不存在, 那么會(huì)自動(dòng)添加
# 但是對(duì)于df.loc[:, ["c", "d"]] = df1,如果c、d不存在,則報(bào)錯(cuò),注意:不是都不存在,而是只要有一個(gè)不存在就報(bào)錯(cuò)
# 如果是指定了不存在的索引,暫時(shí)不會(huì)報(bào)錯(cuò),而是彈出一個(gè)警告
print(df.loc[[1, 11]])
"""
a b
1 2.0 22.0
11 NaN NaN
"""
# 我們看到指定了不存在的索引,那么自動(dòng)為NaN
# 但同時(shí)會(huì)拋出一個(gè)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一下
所以如果想添加多個(gè)字段,可以直接通過df[["c1", "c2"]] = df1的方式,但是注意:右邊寫的df1,所以右邊需要也是一個(gè)DataFrame,并且兩者列數(shù)相等
import pandas as pd
df = pd.DataFrame({"a": [1, 2, 3], "b": [11, 22, 33]})
df1 = pd.DataFrame({"x": ["aa", 22, None], "y": [">>", "^^", "YY"]})
# 這里我們本意是想創(chuàng)建兩個(gè)字段,值都為None
# 但是很遺憾這樣不可以
try:
# 右邊的值也需要是一個(gè)DataFrame
df[["c", "d"]] = None
except Exception as e:
# 我們看到這里也同樣報(bào)出了相應(yīng)的錯(cuò)誤
# 因此只有當(dāng)右邊的值是DataFrame的時(shí)候,df[["c", "d"]]才能具備創(chuàng)建新字段的能力
print(e) # "None of [Index(['c', 'd'], dtype='object')] are in the [columns]"
# 如果想創(chuàng)建多個(gè)新字段,并且還希望通過廣播的方式賦上同一個(gè)值,那么上面做法是行不通的
# 解決辦法是一個(gè)字段一個(gè)字段的創(chuàng)建,這樣百分之百是沒有任何問題的,既可以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
"""
# 除此之外,還有一個(gè)insert方法
# 這個(gè)方法接收:插入的位置、列名、值
# 比如我想在列c的后面插入一個(gè)新列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這個(gè)方法是在本地進(jìn)行操作的
# 關(guān)鍵是第一個(gè)參數(shù),我們希望插在c的后面,那么就必須獲取c所在的索引,當(dāng)然也可以直接數(shù)出來
# 通過columns.get_loc即可獲取,然后再加上1即可
6. ValueError: cannot compute isin with a duplicate axis.
這個(gè)錯(cuò)誤當(dāng)初也是把我搞懵逼了半天,在復(fù)現(xiàn)這個(gè)異常之前,我們先來聊聊非常常用的isin
isin我們一般是對(duì)Series對(duì)象使用,判斷這個(gè)序列中每一個(gè)元素是不是在另一個(gè)序列里面,下面舉例說明:
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
"""
這個(gè)方法是我們經(jīng)常使用的,但是你對(duì)DataFrame使用過isin嗎?我們有時(shí)候需要判斷兩個(gè)序列,看這兩個(gè)序列中的值是否在另外兩個(gè)序列里面。
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
"""
# 我們來分析一下,對(duì)于df1來說,前兩行肯定是沒有問題的
# 但是第三行有點(diǎn)詭異,我們df1的第三行的y列是3,顯然3是在df2的y列當(dāng)中啊,為什么是False
# 同理第4行,"d"不在df2的x列中我們知道,但是y列的4很明顯在df2的y列當(dāng)中,為什么是False
估計(jì)有人猜到了,那就是對(duì)DataFrame使用isin的時(shí)候,多個(gè)列之間并不是獨(dú)立的。事實(shí)上,DataFrame使用isin也是根據(jù)索引來的,我們舉個(gè)栗子
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]})
# 兩個(gè)一模一樣的DataFrame對(duì)象
print(df1.isin(df2))
"""
x y
0 True True
1 True True
2 True True
3 True True
"""
# 結(jié)果沒問題,但是我們將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
"""
# 此時(shí)我們就看到端倪了,對(duì)于DataFrame對(duì)象來講,isin是判斷對(duì)應(yīng)索引的字段的值是否相同
但是問題又來了,因?yàn)檫@樣顯然不是我們期望的結(jié)果。因?yàn)榧词筪f2中存在,但如果索引對(duì)不上的話也沒有任何意義,因此我們可以手動(dòng)設(shè)置索引。
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設(shè)置為索引不就行了,加上drop=False表示設(shè)置索引的同時(shí),還作為列
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]
# 我們看到此時(shí)根據(jù)索引去找,就能夠準(zhǔn)確的定位了
# 不過細(xì)心的人可能已經(jīng)發(fā)現(xiàn)了,這個(gè)索引是由x和y兩列得到的,事實(shí)上索引如果匹配上了,那么值一定是相等的
# 所以此時(shí)就沒必要在進(jìn)行對(duì)比了
# 是的,所以我們可以換一種方法
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中有一個(gè)Index類,Series和DataFrame的索引就是Index類型
# 當(dāng)然Index分為好幾種,但是它們繼承自Index
print(type(df1.index)) # <class 'pandas.core.indexes.range.RangeIndex'>
# 根據(jù)x和y兩列創(chuàng)建Index對(duì)象
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對(duì)象可以像集合一樣,取并集、交集,當(dāng)然此時(shí)我們可以直接使用isin
# 因?yàn)樗鼈冋w變成了一個(gè)元組,也就是說,此時(shí)是一個(gè)一維序列,對(duì)于一維序列可以直接使用isin
# 直接返回一個(gè)numpy中的array
print(index1.isin(index2)) # [ True True True True]
然而這么做有一個(gè)弊端,沒錯(cuò),我要復(fù)現(xiàn)我們上面的異常了
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設(shè)置為索引不就行了,加上drop=False表示設(shè)置索引的同時(shí),還作為列
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.
# 我們對(duì)一個(gè)DataFrame使用isin,那么要求isin里面的DataFrame的索引是不可以重復(fù)的,否則就會(huì)報(bào)出上面這個(gè)錯(cuò)誤
# 解決辦法是使用pd.Index
print(pd.Index(df1[["x", "y"]]).isin(pd.Index(df2[["x", "y"]]))) # [ True False True True]
# 然鵝,我記得pd.Index這種做法也不保險(xiǎn)
# 由于索引的特殊性,好像這種情況我記得也報(bào)錯(cuò),但是目前沒有
# 因此最穩(wěn)妥的辦法是再轉(zhuǎn)成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
"""
# 這種做法是百分之百?zèng)]有問題的
# 忘記說了,df1.isin(df2)的時(shí)候,兩個(gè)列的名稱一定要對(duì)應(yīng)
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這一列,因此相當(dāng)于NaN,所以結(jié)果為False
print(df1[["y"]].isin(df2))
"""
y
0 True
1 False
2 True
3 True
"""
# 會(huì)自動(dòng)找df2中名稱為y的列進(jìn)行比較,因此記得注意列名
# 當(dāng)然由于df1.isin(df2)在索引方面的局限性,我們一般也不會(huì)使用這種方法
# 而是會(huì)將DataFrame的每一個(gè)字段的值拼接成一個(gè)元組,整體得到一個(gè)Series對(duì)象
# 然后對(duì)Series對(duì)象使用isin,這是最正確的做法
7. ValueError: cannot set a frame with no defined index and a scalar
這個(gè)錯(cuò)誤不是很常見,我們來看一下。
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的記錄選出來,然后同時(shí)創(chuàng)建"c"這一列,并設(shè)置對(duì)應(yīng)的記錄為1。如果不滿足條件,那么會(huì)自動(dòng)為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
"""
上面這種賦值方式也是可以的,我們之前說,對(duì)于一維序列,df["xx"]等價(jià)于df.loc[:, "xx"],但實(shí)際上還是有點(diǎn)區(qū)別的,那就是后者要求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
這個(gè)錯(cuò)誤應(yīng)該遇見的比較少,我們看看這種錯(cuò)誤是怎么發(fā)生的。
import pandas as pd
# 我們說通過字典構(gòu)建DataFrame,value應(yīng)該是序列,不應(yīng)該是一個(gè)標(biāo)量
try:
df = pd.DataFrame({"a": 123, "b": None})
except Exception as e:
print(e) # If using all scalar values, you must pass an index
# 如果傳遞標(biāo)量的話,那么應(yīng)該同時(shí)指定一個(gè)index, index是只有一個(gè)元素的列表,里面是一個(gè)索引
df = pd.DataFrame({"a": 123, "b": None}, index=["索引"])
print(df)
"""
a b
索引 123 None
"""
有待發(fā)掘。。。。
總結(jié)
以上是生活随笔為你收集整理的pandas中的那些让人有点懵逼的异常(坑向)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [译]Java 垃圾回收介绍
- 下一篇: oracle修改统计信息收集时间以及窗口