Pandas
- Pandas 是基于NumPy的一種工具,該工具是為解決數據分析任務而創建的, Pandas提供了大量能使我們快速便捷地處理數據的函數和方法。
- Pandas與出色的 Jupyter工具包和其他庫相結合,Python中用于進行數據分析的環境在性能、生產率和協作能力方面都是卓越的。
- Pandas的主要數據結構是 **Series(**一維數據)與 **DataFrame **(二維數據),Pandas的Series和DataFrame在數據分析領域引入了更多高級功能和更豐富的數據結構,特別是需要處理結構化數據、缺失值、時間序列以及進行復雜統計分析時。
導包
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
Series
Series是一種類似于一維數組的對象,由下面兩個部分組成:
- values:一組數據(ndarray類型)
- index:相關的數據索引標簽
Series
可以看作是ndarray數組的升級版,因為它不僅存儲數據,還存儲了數據的索引,這樣就可以通過標簽來訪問數據,而不僅僅是基于位置的訪問。
Series的創建
兩種創建方式:
由列表或NumPy數組創建
list1 = [11, 22, 33, 44]
n = np.array(list1)
type(n) # numpy.ndarrays = Series(n)
display(type(s)) # pandas.core.series.Series
Series屬性: index和values
# values屬性
s.values # array([11, 22, 33, 44])s.index
# RangeIndex(start=0, stop=4, step=1)
list(s.index)# 修改索引
s.index = list("abcd")
s.index = ["魯班", "李白", "諸葛亮", "張飛"]
display(s)# 通過索引取值
s.魯班, s['魯班']# 通過索引修改值
s['魯班'] = 100
通過字典創建
d = {'a': 11,'b': 22,'c': 33,'d': 44
}
s = Series(d)
s.index = list("ABCD")
display(s)d = {'a': np.random.randint(0, 10, size=(2,3)),'b': np.random.randint(0, 10, size=(2,3)),'c': np.random.randint(0, 10, size=(2,3)),'d': np.random.randint(0, 10, size=(2,3))
}
s = Series(d)
display(s)# 創建時同時設置索引
s = Series([1,2,3], index=["張三", '李四', '王五'])
display(s)
Series的索引
可以使用中括號取單個索引(此時返回的是元素類型),或者中括號里一個列表取多個索引(此時返回的仍然是一個Series類型)。分為顯示索引和隱式索引:
(1) 顯式索引:
- 使用index中的元素作為索引值
- 使用.loc[]
注意,此時是閉區間
s = Series({'語文': 150, "數學": 100, "英語": 120, "Python": 99})# 顯式索引:使用索引名字
s['語文'] # int類型
s[['語文', "Python", "數學"]] # Series類型
s[['語文']] # Series類型# .loc[ ]
s.loc['語文']
s.loc[['語文', "Python", "數學"]]
s.loc[['語文']]
(2) 隱式索引:
- 使用整數作為索引值
- 使用.iloc[](推薦)integer location 整數位置
注意,此時是半開區間
s = Series({'語文': 150, "數學": 100, "英語": 120, "Python": 99})# 隱式索引: 使用數字下標
s[0]
s[[0, 2, 1]]
s[[0]]# iloc[ ]
s.iloc[0]
s.iloc[[0, 2, 1]]
s.iloc[[0]]
Series的切片
- Series一維數組切片
s = Series({'語文': 150, "數學": 100, "英語": 120, "Python": 99, "Numpy": 66, "Pandas": 199})# Series是一維數組# 顯式切片: 閉區間
s["數學": "Python"]
s.loc["數學": "Python"]# 隱式切片 : 左閉右開
s[1: 4]
s.iloc[1: 4]
Series的屬性和方法
- shape 形狀
- size 大小
- index 索引
- values 值
- dtype 元素類型
- name 名字
s.shape # 形狀
s.size # 元素個數
s.index # 索引
s.values # 值
s.dtype # 元素類型s = Series([11, 22, 33], name="年齡")
s.name # Series名字
查看首尾數據
- head() 查看前幾條數據,默認5條
- tail() 查看后幾條數據,默認5
# 查看前幾條數據,默認5條
head()
s.head()
s.head(2)# 查看后幾條數據,默認5
tail()
s.tail()
s.tail(2)
檢測缺失數據
- pd.isnull()
- pd.notnull()
- isnull()
- notnull()
# s.isnul()
pd.isnull(s)# s.notnull()
pd.notnull(s)# 可以通過True,Flase過濾數據# 保留不為空的數據
s[ pd.notnull(s) ]
Series的運算
(1) 適用于NumPy的數乘運算也適用于Series
s = Series(np.random.randint(10, 100, size=10))s + 100
s - 100
s * 100
s / 100
s // 10
s ** 2
(2) Series之間的運算
- 在運算中自動對齊索引的數據
- 如果索引不對應,則補NaN
- Series沒有廣播機制
s1 = Series(np.random.randint(10, 100, size=3))
s2 = Series(np.random.randint(10, 100, size=4))
display(s1 + s2)# Numpy中有廣播機制
n1 = np.array(np.random.randint(1, 10, size=(1, 3)))
n2 = np.array(np.random.randint(1, 10, size=(3, 1)))
display(n1 + n2)
- 注意:要想保留所有的index,類似于sql的全連接,保存兩個運算對象的所有index,則需要使用.add()函數,使用NaN補齊運算結果
s1.add(s2, fill_value=100)
DataFrame
DataFrame是一個二維表格型的數據結構,可以看做是由Series組成的字典。DataFrame由按一定順序排列的多列數據組成。設計初衷是將Series的使用場景從一維拓展到多維。DataFrame既有行索引,也有列索引。
- 行索引:index
- 列索引:columns
- 值:values
DataFrame的創建
最常用的方法是傳遞一個字典來創建。DataFrame以字典的鍵作為每一列的索引名稱,以字典的值(一個數組)作為每一列的內容。此外,DataFrame會自動加上每一行的索引,行索引為從0開始的自然數。
同Series一樣,若傳入的列與字典的鍵不匹配,則相應的值為NaN
d = {'name' : ["魯班", '陳咬金', "豬八戒"],'age' : [7, 9, 8],'sex': ['男', '女', '男']
}
df = DataFrame(d)
在創建時,指定數據、行索引和列索引
df = DataFrame(data=np.random.randint(0, 100, size=(4, 6)),index=['小明', "小紅", '小綠', '小黃'],columns=['語文', '數學', '英語', 'Python', 'Numpy', 'Pandas']
)
df
DataFrame屬性和方法
- values 值
- columns 列索引
- index 行索引
- shape 形狀
- head() 查看前幾行數據
- tail() 查看后幾行數據
# 創建DataFrame
df = DataFrame(d, index=["第一行", "第二行", "第三行"])df.values # 值,二維ndarray數組
df.columns # 列索引
df.index # 行索引
df.shape # 形狀# 查看首尾數據
df.head(2)
df.tail(2)
DataFrame的索引
對列進行索引
- 通過類似字典的方式
- 通過屬性的方式
可以將DataFrame的列獲取為一個Series。返回的Series擁有原DataFrame相同的索引,且name屬性也已經設置好了,就是相應的列名。
df = DataFrame(data=np.random.randint(0, 100, size=(4, 6)),index=['小明', "小紅", '小綠', '小黃'],columns=['語文', '數學', '英語', 'Python', 'Numpy', 'Pandas']
)df.語文
df['語文'] # Seriesdf[['語文', 'Python']] # DataFrame
df[['語文']]
對行進行索引
- 使用.loc[ ]加index來進行行索引
- 使用.iloc[ ]加整數來進行行索引
同樣返回一個Series,index為原來的columns。
df = DataFrame(data=np.random.randint(0, 100, size=(4, 6)),index=['小明', "小紅", '小綠', '小黃'],columns=['語文', '數學', '英語', 'Python', 'Numpy', 'Pandas']
)# DataFrame默認取列索引
df.loc['小明'] # Series
df.iloc[0]df.loc[['小明', '小綠']] # DataFrame
df.loc[['小明']]
df.iloc[[0, 2]] # DataFrame
df.iloc[[0]]
對元素索引的方法
df = DataFrame(data=np.random.randint(0, 100, size=(4, 6)),index=['小明', "小紅", '小綠', '小黃'],columns=['語文', '數學', '英語', 'Python', 'Numpy', 'Pandas']
)# 先取列,再取行
df['語文'][1]
df['語文']['小明']# 先取行,再取列
df.iloc[1]['語文'] # 列索引的符號不可以用整數代替
# df.loc['小明', '語文']
DataFrame的切片
【注意】 直接用中括號時:
- 索引表示的是列索引
- 切片表示的是行切片
df = DataFrame(data=np.random.randint(0, 100, size=(4, 6)),index=['小明', "小紅", '小綠', '小黃'],columns=['語文', '數學', '英語', 'Python', 'Numpy', 'Pandas']
)# 索引: 優先使用列索引, 先取行就需要寫loc或iloc
# 切片: 優先按行切片, 和Numpy操作類似# 行: 行名, 行數字索引
# 列: 列名# 行切片
df[1 : 3] # 左閉右開
df['小明' : "小綠"] # 閉區間df.iloc[1 : 3]
df.loc['小明' : "小綠"]# 列切片: 需要使用loc或iloc
df.iloc[:, 1:3]
df.loc[:, "數學": "Python"]df.loc["小明":"小綠", "數學":"Python"]
df.iloc[:3, 1:4]
DataFrame的運算
DataFrame之間的運算
- 在運算中自動對齊索引的數據
- 如果索引不對應,則補NaN
- DataFrame沒有廣播機制
# 創建DataFrame df1 不同人員的各科目成績,月考一
df1 = DataFrame(data={'Python': [100, 90, 80], 'Java': [80, 70, 60], 'PHP': [60, 50, 40]},index=['張飛', "呂布", '關羽']
)# 創建DataFrame df2 不同人員的各科目成績,月考二
df2 = DataFrame(data={'Python': [100, 90, 80, 70], 'Java': [80, 70, 60, 50], 'PHP': [60, 50, 40, 30], 'Go': [40, 30, 20, 10]},index=['張飛', "呂布", '關羽', "劉備"]
)# DataFrame和數的運算
display(df1 + 100)# DataFrame之間的運算
display(df1 + df2)# 自動填充, 再相加
df1.add(df2, fill_value=1000)
Series與DataFrame之間的運算
-
使用Python操作符:以行為單位操作(參數必須是行),對所有行都有效。(類似于numpy中二維數組與一維數組的運算,但可能出現NaN)
-
使用pandas操作函數:
axis=0:以列為單位操作(參數必須是列),對所有列都有效。
axis=1:以行為單位操作(參數必須是行),對所有行都有效。
# 創建DataFrame df1 不同人員的各科目成績,月考一
df1 = DataFrame(data={'Python': [100, 90, 80], 'Java': [80, 70, 60], 'PHP': [60, 50, 40]},index=['張飛', "呂布", '關羽']
)s = Series([100, 10, 1], index=df1.columns)# 直接相加
df1 + s# 使用add函數
df1.add(s)
df1.add(s, axis='columns') # 列
df1.add(s, axis=1) # 第二個維度s = Series([100, 10, 1], index=df1.index)
df1.add(s, axis='index') # 行
df1.add(s, axis=0) # 行
Pandas層次化索引
創建多層行索引
隱式構造
- 最常見的方法是給DataFrame構造函數的index參數傳遞兩個或更多的數組
data = np.random.randint(0, 100, size=(6, 6))index = [['1班', '1班', '1班', '2班', '2班', '2班'],['張三', '李四', '王五', '趙六', '田七', '孫八']
]columns = [['期中', '期中', '期中', '期末', '期末', '期末'],['語文', '數學', '英語', '語文', '數學', '英語']
]df = pd.DataFrame(data=data, index=index, columns=columns)
df
- Series也可以創建多層索引
data = np.random.randint(0, 100, size=6)index = [['1班', '1班', '1班', '2班', '2班', '2班'],['張三', '李四', '王五', '趙六', '田七', '孫八']
]s = pd.Series(data=data, index=index)
s
顯示構造pd.MultiIndex
- 使用數組
data = np.random.randint(0, 100, size=(6, 6))index = pd.MultiIndex.from_arrays( [['1班', '1班', '1班', '2班', '2班', '2班'],['張三', '李四', '王五', '趙六', '田七', '孫八']
] )columns = [['期中', '期中', '期中', '期末', '期末', '期末'],['語文', '數學', '英語', '語文', '數學', '英語']
]df = pd.DataFrame(data=data, index=index, columns=columns)
df
- 使用tuple
data = np.random.randint(0, 100, size=(6, 6))index = pd.MultiIndex.from_tuples( (('1班', '張三'), ('1班', '李四'), ('1班', '王五'), ('2班', '趙六'), ('2班', '田七'), ('2班', '孫八'), )
)columns = [['期中', '期中', '期中', '期末', '期末', '期末'],['語文', '數學', '英語', '語文', '數學', '英語']
]df = pd.DataFrame(data=data, index=index, columns=columns)
df
- 使用product
data = np.random.randint(0, 100, size=(6, 6))# 笛卡爾積:{a, b} {c, d} => {a,c}, {a,d}, {b,c}, {b,d}
index = pd.MultiIndex.from_product( [['1班', '2班'],['張三', '李四', '王五']]
)columns = [['期中', '期中', '期中', '期末', '期末', '期末'],['語文', '數學', '英語', '語文', '數學', '英語']
]df = pd.DataFrame(data=data, index=index, columns=columns)
df
創建多層列索引(同行索引)
除了行索引index,列索引columns也能用同樣的方法創建多層索引
多層索引對象的索引與切片操作
Series的操作【重要】
對于Series來說,直接中括號[]與使用.loc()完全一樣
# (1) 索引
data = np.random.randint(0, 100, size=6)index = [['1班', '1班', '1班', '2班', '2班', '2班'],['張三', '李四', '王五', '趙六', '田七', '孫八']
]s = pd.Series(data=data, index=index)
s# 顯式索引
s['1班']
s.loc['1班']s.loc[['1班']]
s.loc[['1班', '2班']]s['1班']['張三']
s.loc['1班']['張三']
s.loc['1班','張三']
s['1班', '張三']#隱式索引
s[1]
s.iloc[1]
s.iloc[[3, 2]]# (2) 切片
# 顯式切片
s['1班': '1班']
s.loc['1班': '2班']
# s.loc['李四': '田七'] # 沒有結果
# s.loc[('1班', '李四'): ('2班', '田七')] # 報錯, 顯式切片只對最外層索引有效# 建議使用隱式切片
s[1: 4]
s.iloc[1: 5]
DataFrame的操作
- 可以直接使用列名稱來進行列索引
- 使用行索引需要用iloc(),loc()等函數
- 無法直接對二級索引進行索引
data = np.random.randint(0, 100, size=(6, 6))index = pd.MultiIndex.from_product( [['1班', '2班'],['張三', '李四', '王五']]
)
columns = [['期中', '期中', '期中', '期末', '期末', '期末'],['語文', '數學', '英語', '語文', '數學', '英語']
]
df = pd.DataFrame(data=data, index=index, columns=columns)# 獲取元素
df['期中']['數學'][1]df.iloc[1, 3] # 第1行,第3列(索引) 42
df.loc[('1班', '李四'), ('期末', '語文')] # 42# 列索引
df['期中']
df['期中']['數學']
df['期中', '數學']
# df[('期中', '數學')]
# df.期中.數學df.iloc[:, 2]
df.iloc[:, [1,2,3]]# 行索引
df.loc['1班']
df.loc['1班'].loc['張三']
df.loc['1班', '張三']
df.loc[('1班', '張三')]df.iloc[1]
df.iloc[[1]]
df.iloc[[1,2,3]]# 切片
# 行切片
df.iloc[1:5]
df.loc['1班' : '2班']
df.loc[('1班', '李四') : ('2班', '李四')]# 列切片
df.iloc[:, 1: 5]
df.loc[:, '期中': '期末']
# df.loc[:, ('期中', '數學'): ('期末', '數學')] # 報錯
索引的堆疊
使用stack()的時候,level等于哪一個,哪一個就消失,出現在行里。
data = np.random.randint(0, 100, size=(6, 6))
index = pd.MultiIndex.from_product( [['1班', '2班'],['張三', '李四', '王五']]
)
columns = [['期中', '期中', '期中', '期末', '期末', '期末'],['語文', '數學', '英語', '語文', '數學', '英語']
]
df = pd.DataFrame(data=data, index=index, columns=columns)# stack: 列索引變成行索引
df.stack() # 默認level=-1 (倒數第一層),將最里層的列索引 變成行索引
df.stack(level=1) df.stack(level=0) # 將最外層的列索引 變成行索引
使用unstack()的時候,level等于哪一個,哪一個就消失,出現在列里。
# unstack : 將行索引 變成 列索引
df.unstack()
df.unstack(level=1)df.unstack(level=0)
使用fill_value填充
data = np.random.randint(0, 100, size=(6, 6))
index = pd.MultiIndex.from_tuples( (('1班', '張三'), ('1班', '李四'), ('1班', '王五'), ('2班', '趙六'), ('2班', '田七'), ('2班', '孫八'), )
)
columns = [['期中', '期中', '期中', '期末', '期末', '期末'],['語文', '數學', '英語', '語文', '數學', '英語']
]
df2 = pd.DataFrame(data=data, index=index, columns=columns)df2.unstack()
df2.unstack(fill_value=0)
聚合操作
- 需要指定axis
- 和unstack()相反,聚合的時候,axis等于哪一個,哪一個就保留。
data = np.random.randint(0, 100, size=(6, 6))
index = pd.MultiIndex.from_tuples( (('1班', '張三'), ('1班', '李四'), ('1班', '王五'), ('2班', '趙六'), ('2班', '田七'), ('2班', '孫八'), )
)
columns = [['期中', '期中', '期中', '期末', '期末', '期末'],['語文', '數學', '英語', '語文', '數學', '英語']
]
df2 = pd.DataFrame(data=data, index=index, columns=columns)# DataFrame聚合操作:求和,平均值,最大值,最小值....
df3 = df.loc['1班', '期中']# axis : 0表式行index, 1表式列columns
df3.sum()
df3.sum(axis=0) # 對同一列的多行進行求和
df3.sum(axis=1) # 對同一行的多列進行求和# 多層索引聚合操作
df.sum() # 默認是對同一列的多行求和
df.sum(axis=1) # 對同一行的多列求和
df.sum(axis=0, level=0) # 表式 行 索引中的最外層
df.sum(axis=1, level=0) # 表式 列 索引中的最外層
df.sum(axis=0, level=1) # 表式 行 索引中的最里層
df.sum(axis=1, level=1) # 表式 列 索引中的最里層
Pandas數據合并
- pd.concat
- pd.append
- pd.merge
為了方便,我們首先定義一個生成DataFrame的函數:
def make_df(indexs, columns): data = [[str(j)+str(i) for j in columns] for i in indexs]df = pd.DataFrame(data=data, index=indexs, columns=columns)return df# 調用
# make_df([1, 2, 3, 4], list('ABCD'))
使用pd.concat()級聯
Pandas使用pd.concat函數,與NumPy中的concatenate函數類似,只是多了一些參數:
簡單級聯
df1 = make_df([1, 2], ['A', 'B'])
df2 = make_df([3, 4], ['A', 'B'])
display(df1, df2)# 上下合并,垂直合并
pd.concat([df1, df2]) # 左右合并,水平合并
pd.concat([df1, df2], axis=1) # 忽略行索引,重置行索引
pd.concat([df1, df2], ignore_index=True) # 使用多層索引 keys
pd.concat([df1, df2], keys=['x', 'y'])
# pd.concat([df1, df2], keys=['x', 'y'], axis=1)
不匹配級聯
不匹配指的是級聯的維度的索引不一致。例如縱向級聯時列索引不一致,橫向級聯時行索引不一致
df3 = make_df([1, 2, 3, 4], ['A', 'B', 'C', 'D'])
df4 = make_df([2, 3, 4, 5], ['B', 'C', 'D', 'E'])
display(df3, df4)# 對應索引沒有值的會自動用NaN填充
pd.concat([df3, df4])# 外連接:補NaN(默認模式), 默認值outer,類似并集, 所有數據都會顯示
pd.concat([df3, df4])
pd.concat([df3, df4], join='outer')# 內連接:只連接匹配的項, 交集, 只顯示共同的列或行
pd.concat([df3, df4], join='inner')
使用append()函數添加
由于在后面級聯的使用非常普遍,因此有一個函數append專門用于在后面添加
df3 = make_df([1, 2, 3, 4], ['A', 'B', 'C', 'D'])
df4 = make_df([2, 3, 4, 5], ['B', 'C', 'D', 'E'])
display(df3, df4)df3.append(df4, sort=True)
使用merge()合并
- 類似MySQL中表和表之間的合并
- merge與concat的區別在于,merge需要依據某一共同的行或列來進行合并
- 使用pd.merge()合并時,會自動根據兩者相同column名稱的那一列,作為key來進行合并。
- 每一列元素的順序不要求一致
一對一合并
df1 = pd.DataFrame({'name': ['張三', '李四', '王五'],'id' : [1, 2, 3],'age': [22, 33, 44]
})
df2 = pd.DataFrame({'id' : [2, 3, 4],'sex': ['男', '女', '女'],'job': ['saler', 'CTO', 'Programer']
})
display(df1, df2)# 合并
# pd.merge(df1, df2)
df1.merge(df2)
多對一合并
df1 = pd.DataFrame({'name': ['張三', '李四', '王五'],'id' : [1, 2, 2],'age': [22, 33, 44]
})
df2 = pd.DataFrame({'id' : [2, 3, 4],'sex': ['男', '女', '女'],'job': ['saler', 'CTO', 'Programer']
})
display(df1, df2)# 合并
df1.merge(df2)
多對多合并
df1 = pd.DataFrame({'name': ['張三', '李四', '王五'],'id' : [1, 2, 2],'age': [22, 33, 44]
})
df2 = pd.DataFrame({'id' : [2, 2, 4],'sex': ['男', '女', '女'],'job': ['saler', 'CTO', 'Programer']
})
display(df1, df2)# 合并
df1.merge(df2)
key的規范化
- 使用on=顯式指定哪一列為key,當有多個key相同時使用
df1 = pd.DataFrame({'name': ['張三', '李四', '王五'],'id' : [1, 2, 3],'age': [22, 33, 44]
})
df2 = pd.DataFrame({'id' : [2, 3, 4],'name': ['李四', '王五', '趙六'],'job': ['saler', 'CTO', 'Programer']
})
display(df1, df2)# 如果有多列名稱相同, 則需要指定一列作為連接的字段
df1.merge(df2, on='id')
df1.merge(df2, on='name')
- 使用left_on和right_on指定左右兩邊的列作為key,當左右兩邊的key都不想等時使用
# 如果沒有相同的列名,則需要使用left_on和right_on來分別指定2個表的列作為連接的字段
df1.merge(df2, left_on='id', right_on='id2')
- 當左邊的列和右邊的index相同的時候,使用right_index=True
df1.merge(df2, left_index=True, right_on='id2')
內合并與外合并
- 內合并:只保留兩者都有的key(默認模式)
df1 = pd.DataFrame({'name': ['張三', '李四', '王五'],'id' : [1, 2, 3],'age': [22, 33, 44]
})
df2 = pd.DataFrame({'id' : [2, 3, 4],'sex': ['男', '女', '女'],'job': ['saler', 'CTO', 'Programer']
})
display(df1, df2)# 內連接: inner join
df1.merge(df2)
df1.merge(df2, how='inner')
- 外合并 how=‘outer’:補NaN
# 外連接:
df1.merge(df2, how='outer')
- 左合并、右合并:how=‘left’,how=‘right’
# 左連接:
df1.merge(df2, how='left')# 右連接:
df1.merge(df2, how='right')
添加后綴
- 當列沖突時,即有多個列名稱相同時,需要使用on=來指定哪一個列作為key,配合suffixes指定沖突列名
- 可以使用suffixes=自己指定后綴
df1 = pd.DataFrame({'name': ['張三', '李四', '王五'],'id' : [1, 2, 3],'age': [22, 33, 44]
})
df2 = pd.DataFrame({'id' : [2, 3, 4],'name': ['李四', '王五', '趙六'],'job': ['saler', 'CTO', 'Programer']
})
display(df1, df2)df1.merge(df2, on='id', suffixes=['_表1', '_表2'])
merge合并總結
- 合并有三種現象: 一對一, 多對一, 多對多.
- 合并默認會找相同的列名進行合并, 如果有多個列名相同,用on來指定.
- 如果沒有列名相同,但是數據又相同,可以通過left_on, right_on來分別指定要合并的列.
- 如果想和index合并, 使用left_index, right_index來指定.
- 如果多個列相同,合并之后可以通過suffixes來區分.
- 還可以通過how來控制合并的結果, 默認是內合并, 還有外合并outer, 左合并left, 右合并right.
Pandas缺失值處理
None
-
None是Python自帶的,是Python中的空對象。None不能參與到任何計算中。
-
object類型的運算要比int類型的運算慢得多
# 計算不同數據類型求和時間
%timeit np.arange(1e5, dtype=object).sum()
# 6.1 ms ± 122 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)%timeit np.arange(1e5, dtype=np.int32).sum()
# 134 μs ± 7.16 μs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
np.nan(NaN)
- np.nan是浮點類型,能參與到計算中。但計算的結果總是NaN。
type(np.nan)
# float
- 但可以使用np.nan*()函數來計算nan,此時會過濾掉nan。
n = np.array([1, 2, 3, np.nan, 5, 6])# np.sum(n) # nan
np.nansum(n)np.nan + 10
pandas中的None與NaN
pandas中None與np.nan都視作np.nan
- 創建DataFrame
data = np.random.randint(0 ,100, size=(5, 5))
df = pd.DataFrame(data=data, columns=list('ABCDE'))# 修改數據,增加2種nan
df.loc[2, 'B'] = np.nan
df.loc[3, 'C'] = None
display(df)# 查看結果
df.loc[2, 'B'] # nan
df.loc[3, 'C'] # nan
pandas中None與np.nan的操作
- isnull()
- notnull()
- all()
- any()
- dropna(): 過濾丟失數據
- fillna(): 填充丟失數據
(1)判斷函數
- isnull()
- notnull()
# isnull
df.isnull()# notnull
df.notnull()# all(): 必須全部為True才為True, 類似and
# any(): 只要有一個為True即為True, 類似ordf.isnull().any() # 常用, 盡可能找到所有的空值
# df.isnull().all()df.notnull().all() # 常用,盡可能找到所有的空值
# df.notnull().any()df.isnull().any(axis=1) # axis=1 表式列,判斷一行中的每一列數據進行判斷
df.notnull().all(axis=1)
- 使用bool值索引過濾數據
# 行過濾
# 將df中有空的列過濾掉
cond = df.isnull().any(axis=1)
# display(cond, ~cond)
# ~ 取反
df[~cond]cond = df.notnull().all(axis=1)
# cond
df[cond]# 列過濾
cond = df.notnull().all()
# cond
df.loc[:, cond]cond = df.isnull().any()
# cond
df.loc[:, ~cond]
(2) 過濾函數
dropna()
可以選擇過濾的是行還是列(默認為行)
df.dropna() # 默認是刪除有空的行
df.dropna(axis=1) # 刪除有空的列
也可以選擇過濾的方式 how = ‘all’
df.dropna(how='any') # 默認值,默認有空就會刪除
df.dropna(how='all', axis=1) # 所有的值都為空(整行或整列為空),才刪除
inplace=True 修改原數據
df2 = df.copy()# inplace=True: 表式修改原數據
df2.dropna(inplace=True)
df2
(3) 填充函數 Series/DataFrame
fillna()
# 填充
df.fillna(value=100)df2 = df.copy()
df2.loc[1, 'B'] = np.nan
df2.loc[4, 'C'] = None
display(df2)# limit: 限制對應維度上填充的次數
df2.fillna(value=100, limit=1, inplace=True)# 可以選擇前向填充還是后向填充
# method : {'backfill', 'bfill', 'pad', 'ffill', None}, default None
# Method to use for filling holes in reindexed Series
# pad/ffill: propagate last valid observation forward to next valid
# backfill/bfill: use next valid observation to fill gap.df.fillna(method='ffill') # 用上面數據來填充自己
# df.fillna(method='pad')df.fillna(method='bfill') # 用下面數據來填充自己
# df.fillna(method='backfill')df.fillna(method='ffill', axis=1) # 用左邊數據來填充自己df.fillna(method='bfill', axis=1) # 用右邊數據來填充自己
Pandas處理重復值和異常值
刪除重復行
- 使用duplicated()函數檢測重復的行,返回元素為布爾類型的Series對象
- 每個元素對應一行,如果該行不是第一次出現,則元素為True
# 讓第一行和第二行重復
df.loc[1] = df.loc[2]# 判斷是否和前面的行重復了
df.duplicated()# df.duplicated(keep='first') # 保留第一行
# df.duplicated(keep='last') # 保留最后一行
# df.duplicated(keep=False) # 標記所有重復行df.loc[1, 'D'] = 'DDD'
# subset: 子集, 只需要子集相同就可以判斷重復
df.duplicated(subset=['A', 'B', 'C'])
使用drop_duplicates()函數刪除重復的行
df.drop_duplicates(subset=['A', 'B', 'C'])
df.drop_duplicates(subset=['A', 'B', 'C'], keep='last')
映射
映射的含義:創建一個映射關系列表,把values元素和一個特定的標簽或者字符串綁定
包含三種操作:
- replace()函數:替換元素
- map()函數:處理某一單獨的列, 最重要
- rename()函數:替換索引
replace()函數:替換元素
使用replace()函數,對values進行替換操作
index = ['魯班', '張三豐', '張無忌', '杜甫', '李白']
columns = ['Python', 'Java', 'H5', 'Pandas']
data = np.random.randint(0, 100, size=(5, 4))df = pd.DataFrame(data=data, index=index, columns=columns)# replace還經常用來替換NaN元素
df.replace({1: 100})
map()函數:
適合處理某一單獨的列
df2 = df.copy()
df2# map是Series調用,不能使用DataFrame調用
df2['Python'].map({12: 100, 11: 90})# map()函數中可以使用lambda函數
# 新建一列
df2['NumPy'] = df2['Python'].map(lambda x: x+100)
df2# 新增一列:判斷java成績是否及格
df2['是否及格'] = df2['Java'].map(lambda n: "及格" if n>=60 else "不及格")
df2# 新增一列:判斷Java成績的等級(>=80優秀,>=60及格,<60不及格)
def fn(n):if n >= 80:return '優秀'elif n >= 60:return '及格'return '不及格'df2['等級'] = df2['Java'].map(fn)
df2
rename()函數:替換索引
df3 = df.copy()
df3# 更改索引名稱
df3.rename({'魯班': "Mr Lu"}) # 默認更改行索引
df3.rename({'Python': 'PYTHON'}, axis=1) # 更改列索引df3.rename(index={'魯班': "Mr Lu"}) # 更改行索引
df3.rename(columns={'Python': 'PYTHON'}) # 更改列索引
apply()函數:
既支持 Series,也支持 DataFrame
df = pd.DataFrame(data=np.random.randint(0, 10, size=(5,3)),index=list('ABCDE'),columns=['Python', 'NumPy', 'Pandas'])# 用于Series,其中x是Series中元素
df['Python'].apply(lambda x:True if x >5 else False) # 用于DataFrame,其中的x是DataFrame中列或者行,是Series
df.apply(lambda x : x.median(), axis=0) # 列的中位數
df.apply(lambda x : x.median(), axis=1) # 行的中位數# 自定義方法
def convert(x): return (np.round(x.mean(), 1), x.count())
df.apply(convert, axis=1) # 行平均值,計數# applymap: DataFrame專有, 其中的x是DataFrame中每個元素
df.applymap(lambda x : x + 100) # 計算DataFrame中每個元素
transform()函數
df = pd.DataFrame(data=np.random.randint(0, 10, size=(10,3)),index=list('ABCDEFHIJK'),columns=['Python', 'NumPy', 'Pandas'])# 1、一列執行多項計算
df['Python'].transform([np.sqrt, np.exp]) # Series處理# 2、多列執行不同計算
def convert(x):if x.mean() > 5:x *= 10else:x *= -10return xdf.transform({'Python':convert,'NumPy':np.max,'Pandas':np.min})
異常值檢測和過濾
- describe(): 查看每一列的描述性統計量
# 查看每一列的描述性統計
df.describe()
df.describe([0.3, 0.4, 0.5, 0.9, 0.99]) # 指定百分位數
df.describe([0.3, 0.4, 0.5, 0.9, 0.99]).T # 轉置,行和列轉換,在列比較多的情況下使用
- df.std() : 可以求得DataFrame對象每一列的標準差
df.std()
- df.drop(): 刪除特定索引
df4 = df.copy()
df4df4.drop('魯班') # 默認刪除行
df4.drop('Java', axis=1) # 刪除列df4.drop(index='魯班') # 刪除行
df4.drop(columns='H5') # 刪除列df4.drop(columns=['Java', 'Pandas']) # 同時刪除多列
- unique() : 唯一,去重
index = ['魯班', '張三豐', '張無忌', '杜甫', '李白']
columns = ['Python', 'Java', 'H5', 'Pandas']
data = np.random.randint(0, 10, size=(5, 4))df = pd.DataFrame(data=data, index=index, columns=columns)
df# unique() : 要用于Series, 不能用于DataFrame
df['Python'].unique()
- query() : 按條件查詢
# ==, >, <,
# in
# and &
# or |df.query('Python == 5')
df.query('Python > 5')df.query('Python==5 and Java==5')
df.query('Python==5 & Java==5')
df.query('Python==5 or Java==6')
df.query('Python==5 | Java==6')# 使用變量
n = 5
df.query('Python > @n') # @n 使用變量
df.query('Python in [3, 4, 5, 6]')
m = [3, 4, 5, 6]
df.query('Python in @m')
-
df.sort_values(): 根據值排序
-
df.sort_index(): 根據索引排序
index = ['魯班', '張三豐', '張無忌', '杜甫', '李白']
columns = ['Python', 'Java', 'H5', 'Pandas']
data = np.random.randint(0, 100, size=(5, 4))df = pd.DataFrame(data=data, index=index, columns=columns)
dfdf.sort_values('Python') # 默認按照列名排序,默認升序
df.sort_values('Python', ascending=False) # 降序
df.sort_values('魯班', axis=1) # 按照行索引或列索引排序
df.sort_index(ascending=True, axis=1)
- df.info(): 查看數據信息
df.info()
抽樣
-
使用.take()函數排序
-
可以借助np.random.permutation()函數隨機排序
# 使用前面的df2
df2.take([4, 1, 2, 3, 0]) # 行排列
df2.take([1, 0, 2, 3, 4, 5, 6], axis=1) # 列排列# 隨機排序
np.random.permutation([0, 1, 2, 3, 4, 5])# 無放回抽樣:依次拿取,沒有重復值
df2.take(np.random.permutation([0, 1, 2, 3, 4]))# 有放回抽樣: 可能出現重復值
# 當DataFrame規模足夠大時,直接使用np.random.randint()函數,就配合take()函數實現隨機抽樣
np.random.randint(0, 10, size=10)
df2.take(np.random.randint(0, 5, size=5))
Pandas數學函數
- 聚合函數
df = pd.DataFrame(data=np.random.randint(0,100,size = (20,3)))df.count() # 非空值的數量
df.max() # 最大值,axis=0/1
df.min() # 最小值, axis=0/1
df.median() # 中位數
df.sum() # 求和
df.mean(axis=1) # 每一行的平均值df[0].value_counts() # 統計元素出現次數
df.cumsum() # 累加
df.cumprod() # 累乘# 方差: 當數據分布比較分散(即數據在平均數附近波動較大)時,各個數據與平均數的差的平方和較大,方差就較大;當數據分布比較集中時,各個數據與平均數的差的平方和較小。因此方差越大,數據的波動越大;方差越小,數據的波動就越小
df.var() # 方差# 標準差 = 方差的算術平方根
df.std() # 標準差
其他數學函數
協方差
- 兩組數值中每對變量的偏差乘積的平均值
- 協方差>0 : 表式兩組變量正相關
- 如果兩個變量的變化趨勢一致,也就是說如果其中一個大于自身的期望值時另外一個也大于自身的期望值,那么兩個變量之間的協方差就是正值;
- 協方差<0 : 表式兩組變量負相關
- 如果兩個變量的變化趨勢相反,即其中一個變量大于自身的期望值時另外一個卻小于自身的期望值,那么兩個變量之間的協方差就是負值。
- 協方差=0 : 表式兩組變量不相關
相關系數
- 相關系數r = X與Y的協方差 / (X的標準差 * Y的標準差)
- 相關系數值的范圍在-1和+1之間
- r>0為正相關,r<0為負相關。r=0表示不相關
- r 的絕對值越大,相關程度越高
- 兩個變量之間的相關程度,一般劃分為四級:
- 如兩者呈正相關,r呈正值,r=1時為完全正相關;
- 如兩者呈負相關則r呈負值,而r=-1時為完全負相關,完全正相關或負相關時,所有圖點都在直線回歸線上;點分布在直線回歸線上下越離散,r的絕對值越小。
- 相關系數的絕對值越接近1,相關越密切;越接近于0,相關越不密切。
- 當r=0時,說明X和Y兩個變量之間無直線關系。
- 通常|r|大于0.8時,認為兩個變量有很強的線性相關性。
# 協方差
# 兩組數值中每對變量的偏差乘積的平均值df.cov()
df[0].cov(df[1]) # 第0列 和 第1列的協方差# 相關系數 = X與Y的協方差 / (X的標準差 * Y的標準差)
df.corr() # 所有屬性相關性系數
df.corrwith(df[2]) # 單一屬性相關性系數
協方差: C o v ( X , Y ) = ∑ 1 n ( X i ? X  ̄ ) ( Y i ? Y  ̄ ) n ? 1 Cov(X,Y) = \frac{\sum\limits_1^n(X_i - \overline{X})(Y_i - \overline{Y})}{n-1} Cov(X,Y)=n?11∑n?(Xi??X)(Yi??Y)?
相關性系數: r ( X , Y ) = C o v ( X , Y ) V a r [ X ] ? V a r [ Y ] r(X,Y) = \frac{Cov(X,Y)}{\sqrt{Var[X]*Var[Y]}} r(X,Y)=Var[X]?Var[Y]?Cov(X,Y)?
數據分組聚合
數據聚合是數據處理的最后一步,通常是要使每一個數組生成一個單一的數值。
分組
數據聚合處理:
- 分組:先把數據分為幾組
- 用函數處理:為不同組的數據應用不同的函數以轉換數據
- 合并:把不同組得到的結果合并起來
數據分類處理的核心: groupby()函數
# 創建DataFrame
df = pd.DataFrame({'color': ['green', 'green', 'yellow', 'blue', 'blue', 'yellow', 'yellow'],'price': [4, 5, 3, 2, 7, 8, 9]}
)
df# 使用.groups屬性查看各行的分組情況:
# 根據color進行分組
df.groupby(by='color')
df.groupby(by='color').groups# 分組 + 聚合
df.groupby(by='color').sum()
分組聚合練習:
假設菜市場張大媽在賣菜,有以下屬性:
-
菜品(item):蘿卜,白菜,辣椒,冬瓜
-
顏色(color):白,青,紅
-
重量(weight)
-
價格(price)
- 要求以屬性作為列索引,新建一個ddd
- 對ddd進行聚合操作,求出顏色為白色的價格總和
- 對ddd進行聚合操作,求出蘿卜的所有重量以及平均價格
- 使用merge合并總重量及平均價格
ddd = pd.DataFrame(data={"item": ["蘿卜","白菜","辣椒","冬瓜","蘿卜","白菜","辣椒","冬瓜"],'color':["白","青","紅","白","青","紅","白","青"],'weight': [10,20,10,10,30,40,50,60],'price': [0.99, 1.99, 2.99, 3.99, 4, 5, 6,7]}
)
ddd# 2. 對ddd進行聚合操作,求出顏色為白色的價格總和
ddd.groupby('color')['price'].sum() # Series
ddd.groupby('color')[['price']].sum() # DataFrame
ddd.groupby('color')[['price']].sum() .loc[['白']]# 3. 對ddd進行聚合操作,求出蘿卜的所有重量以及平均價格
df1 = ddd.groupby('item')[['weight']].sum()
df2= ddd.groupby('item')[['price']].mean()# 4.使用merge合并總重量及平均價格
# display(df1, df2)
df1.merge(df2, left_index=True, right_index=True)
Pandas加載數據
csv數據
data = np.random.randint(0,50,size=(10,5))
df = pd.DataFrame(data=data, columns=['Python','Java','Go','C','JS'])# 保存到csv
df.to_csv('data.csv',sep=',', # 文本分隔符,默認是逗號header=True, # 是否保存列索引# 是否保存行索引,保存行索引,文件被加載時,默認行索引會作為一列index=True) # 加載csv數據
pd.read_csv('data.csv',sep=',', # 默認是逗號header=[0], # 指定列索引index_col=0) # 指定行索引pd.read_table('data.csv', # 和read_csv類似,讀取限定分隔符的文本文件sep=',',header=[0], # 指定列索引index_col=0 # 指定行索引)
excel數據
data = np.random.randint(0, 50, size=(10,5))
df = pd.pDataFrame(data=data, columns=['Python','Java','Go','C','JS'])# 保存到excel文件
df.to_excel('data.xls',sheet_name='sheet1',# Excel中工作表的名字header=True, # 是否保存列索引index=False) # 是否保存行索引# 讀取excel
pd.read_excel('data.xls',sheet_name=0, # 讀取哪一個Excel中工作表,默認第一個, 也可以寫工作表名稱header=0, # 使用第一行數據作為列索引names=list('ABCDE'), # 替換行索引index_col=1) # 指定行索引,B作為行索引
MySQL數據
- 需要安裝pymysql
- pip install pymysql -i https://pypi.tuna.tsinghua.edu.cn/simple
- 需要安裝sqlalchemy:
- pip install sqlalchemy -i https://pypi.tuna.tsinghua.edu.cn/simple
- sqlalchemy是Python語言下的數據庫引擎庫, 在Django/Flask中常用ORM庫
from sqlalchemy import create_enginedata = np.random.randint(0,150,size=(150,3))
df = pd.DataFrame(data=data, columns=['Python','Pandas','PyTorch'])# 數據庫連接
# root: MySQL用戶名
# 123456: MySQL密碼
# localhost: 本機
# db: 數據庫名(提前創建)
conn = create_engine('mysql+pymysql://root:123456@localhost/db')# 保存到MySQL
df.to_sql('score', # 表名(會自動創建表)conn, # 數據庫連接index=False, # 是否保存行索引if_exists='append') # 如果表名存在,追加數據# 從MySQL中加載數據
pd.read_sql('select * from score', # sql查詢語句conn, # 數據庫連接index_col='Python') # 指定行索引名
Pandas分箱操作
- 分箱操作就是將連續型數據離散化。
- 分箱操作分為等距分箱和等頻分箱。
data = np.random.randint(0,150,size=(150,3))
df = pd.DataFrame(data=data, columns=['Python','Pandas','PyTorch'])# 對Python列進行分箱
df.Python.values# 1、等寬分箱
pd.cut(df.Python, bins=4)# 指定寬度分箱
pd.cut(df.Python, # 分箱數據bins=[0, 30, 60, 80, 100], # 分箱斷點right=False, # 左閉右開labels=['D','C','B','A']) # 分箱后分類標簽# 2、等頻分箱
pd.qcut(df.Python, # 分箱數據q=4, # 4等份labels=['差', '中', '良', '優']) # 分箱后分類標簽