第 3 章 Pandas
3.1 什么是Pandas
Pandas 是一個開源的數據分析和數據處理庫,它是基于 Python 編程語言的。
Pandas 提供了易于使用的數據結構和數據分析工具,特別適用于處理結構化數據,如表格型數據(類似于Excel表格)。
Pandas 是數據科學和分析領域中常用的工具之一,它使得用戶能夠輕松地從各種數據源中導入數據,并對數據進行高效的操作和分析。
用得最多的pandas對象是Series,一個一維的標簽化數組對象,另一個是DataFrame,它是一個面向列的二維表結構。
pandas兼具numpy高性能的數組計算功能以及電子表格和關系型數據庫(如SQL)靈活的數據處理功能。它提供了復雜精細的索引功能,能更加便捷地完成重塑、切片和切塊、聚合以及選取數據子集等操作。
pandas功能:
? 有標簽軸的數據結構
在數據結構中,每個軸都被賦予了特定的標簽,這些標簽用于標識和引用軸上的數據元素,使得數據的組織、訪問和操作更加直觀和方便
? 集成時間序列功能。
? 相同的數據結構用于處理時間序列數據和非時間序列數據。
? 保存元數據的算術運算和壓縮。
? 靈活處理缺失數據。
? 合并和其它流行數據庫(例如基于SQL的數據庫)的關系操作。
pandas這個名字源于panel data(面板數據,這是多維結構化數據集在計量經濟學中的術語)以及Python data analysis(Python數據分析)。
3.2 Pandas數據結構-Series
Series 是 Pandas 中的一個核心數據結構,類似于一個一維的數組,具有數據和索引。
Series 可以存儲任何數據類型(整數、浮點數、字符串等),并通過標簽(索引)來訪問元素。Series 的數據結構是非常有用的,因為它可以處理各種數據類型,同時保持了高效的數據操作能力,比如可以通過標簽來快速訪問和操作數據。
1)Series 特點:
? 一維數組:Series 中的每個元素都有一個對應的索引值。
? 索引: 每個數據元素都可以通過標簽(索引)來訪問,默認情況下索引是從 0 開始的整數,但你也可以自定義索引。
? 數據類型: Series 可以容納不同數據類型的元素,包括整數、浮點數、字符串、Python 對象等。
? 大小不變性:Series 的大小在創建后是不變的,但可以通過某些操作(如 append 或 delete)來改變。
? 操作:Series 支持各種操作,如數學運算、統計分析、字符串處理等。
? 缺失數據:Series 可以包含缺失數據,Pandas 使用NaN(Not a Number)來表示缺失或無值。
? 自動對齊:當對多個 Series 進行運算時,Pandas 會自動根據索引對齊數據,這使得數據處理更加高效。
我們可以使用 Pandas 庫來創建一個 Series 對象,并且可以為其指定索引(Index)、名稱(Name)以及值(Values):
3.2.2 Series的創建
1)先安裝pandas包,如果在Pycharm中加載不出來,可以通過如下命令安裝
C:\Users\fuxiaofeng>conda activate python-2025-conda
(python-2025-conda) C:\Users\fuxiaofeng>conda install pandas
2)直接通過列表創建Series
import pandas as pd
s = pd.Series([4, 7, -5, 3])
print(s)
0 4
1 7
2 -5
3 3
dtype: int64
Series的字符串表現形式為:索引在左邊,值在右邊。由于我們沒有為數據指定索引,于是會自動創建一個0到N-1(N為數據的長度)的整數型索引。
3)通過列表創建Series時指定索引
s = pd.Series([4, 7, -5, 3], index=[“a”, “b”, “c”, “d”])
print(s)
a 4
b 7
c -5
d 3
dtype: int64
4)通過列表創建Series時指定索引和名稱
s = pd.Series([4, 7, -5, 3], index=[“a”, “b”, “c”, “d”],name=“hello_python”)
print(s)
a 4
b 7
c -5
d 3
Name: hello_python, dtype: int6
5)直接通過字典創建Series
dic = {“a”: 4, “b”: 7, “c”: -5, “d”: 3}
s = pd.Series(dic)
print(s)
a 4
b 7
c -5
d 3
dtype: int64
s1 = pd.Series(dic,index=[“a”,“c”],name=“aacc”)
print(s1)
a 4
c -5
Name: aacc, dtype: int64
3.2.3 Series的常用屬性
屬性 說明
index Series的索引對象
values Series的值
ndim Series的維度
shape Series的形狀
size Series的元素個數
dtype或dtypes Series的元素類型
name Series的名稱
loc[] 顯式索引,按標簽索引或切片
iloc[] 隱式索引,按位置索引或切片
at[] 使用標簽訪問單個元素
iat[] 使用位置訪問單個元素
import pandas as pd
arrs = pd.Series([11,22,33,44,55],name=“atguigu”,index=[“a”,“b”,“c”,“d”,“e”])
print(arrs)
index Series的索引對象
print(arrs.index)
for i in arrs.index:
print(i)
values Series的值
print(arrs.values)
ndim Series的維度
print(arrs.ndim)
shape Series的形狀
print(arrs.shape)
size Series的元素個數
print(arrs.size)
dtype或dtypes Series的元素類型
print(arrs.dtype)
print(arrs.dtypes)
name Series的名稱
print(arrs.name)
loc[] 顯式索引,按標簽索引或切片
print(arrs.loc[“c”])
print(arrs.loc[“c”:“d”])
iloc[] 隱式索引,按位置索引或切片
print(arrs.iloc[0])
print(arrs.iloc[0:3])
at[] 使用標簽訪問單個元素
print(arrs.at[“a”])
iat[] 使用位置訪問單個元素
print(arrs.iat[3])
3.2.4 Series的常用方法
方法 說明
head() 查看前n行數據,默認5行
tail() 查看后n行數據,默認5行
isin() 元素是否包含在參數集合中
isna() 元素是否為缺失值(通常為 NaN 或 None)
sum() 求和,會忽略 Series 中的缺失值
mean() 平均值
min() 最小值
max() 最大值
var() 方差
std() 標準差
median() 中位數
mode() 眾數(出現頻率最高的值),如果有多個值出現的頻率相同且都是最高頻率,這些值都會被包含在返回的 Series 中
quantile(q,interpolation) 指定位置的分位數
q的取值范圍是 0 到 1 之間的浮點數或浮點數列表,如quantile(0.5)表示計算中位數(即第 50 百分位數);
interpolation:指定在計算分位數時,如果分位數位置不在數據點上,采用的插值方法。默認值是線性插值 ‘linear’,還有其他可選值如 ‘lower’、‘higher’、‘midpoint’、‘nearest’ 等
describe() 常見統計信息
value_count() 每個元素的個數
count() 非缺失值元素的個數,如果要包含缺失值,用len()
drop_duplicates() 去重
unique() 去重后的數組
nunique() 去重后元素個數
sample() 隨機采樣
sort_index() 按索引排序
sort_values() 按值排序
replace() 用指定值代替原有值
to_frame() 將Series轉換為DataFrame
equals() 判斷兩個Series是否相同
keys() 返回Series的索引對象
corr() 計算與另一個Series的相關系數
默認使用皮爾遜相關系數(Pearson correlation coefficient)來計算相關性。要求參與比較的數組元素類型都是數值型。
當相關系數為 1 時,表示兩個變量完全正相關,即一個變量增加,另一個變量也隨之增加。
當相關系數為 -1 時,表示兩個變量完全負相關,即一個變量增加,另一個變量隨之減少。
當相關系數為 0 時,表示兩個變量之間不存在線性相關性。
例如,分析某地區的氣溫和冰淇淋銷量之間的關系
cov() 計算與另一個Series的協方差
hist() 繪制直方圖,用于展示數據的分布情況。它將數據劃分為若干個區間(也稱為 “bins”),并統計每個區間內數據的頻數。
需要安裝matplotlib包
items() 獲取索引名以及值
import pandas as pd
import numpy as np
arrs = pd.Series([11,22,np.nan,None,44,22],index=[‘a’,‘b’,‘c’,‘d’,‘e’,‘f’])
head() 查看前n行數據,默認5行
print(arrs.head())
tail() 查看后n行數據,默認5行
print(arrs.tail(3))
isin() 判斷數組中的每一個元素是否包含在參數集合中
print(arrs.isin([11]))
isna() 元素是否為缺失值
print(arrs.isna())
sum() 求和,會忽略 Series 中的缺失值
print(arrs.sum())
mean() 平均值
print(arrs.mean())
min() 最小值
print(arrs.min())
max() 最大值
print(arrs.max())
var() 方差
print(arrs.var())
std() 標準差
print(arrs.std())
print(arrs.var())
median() 中位數
若數據集的元素個數為奇數,中位數就是排序后位于中間位置的數值。
若數據集的元素個數為偶數,中位數則是排序后中間兩個數的平均值。
去除缺失值之后,arrs 就變成了 [11, 22, 44, 22]。
對 [11, 22, 44, 22] 進行排序,得到 [11, 22, 22, 44]
print(arrs.median())
mode() 眾數
print(arrs.mode())
quantile() 指定位置的分位數,如quantile(0.5)
分位數:分位數是把一組數據按照從小到大的順序排列后,分割成若干等份的數值點。
0.25 分位數就是將數據從小到大排序后,位于 25% 位置處的數值。
插值方法:當計算分位數時,若位置不是整數,就需要借助插值方法來確定分位數值。# “midpoint” 插值方法是指當分位數位置處于兩個數據點之間時,取這兩個數據點的
平均值作為分位數值。
對于有 n個數據點的有序數據集,q分位數的位置 i可以通過公式 i=(n?1)q來計
算。這里 n=4,q=0.25,則 i=(4?1)×0.25=0.75。這意味著 0.25 分位數處于第一個# 數據點(值為 11)和第二個數據點(值為 22)之間。使用 “midpoint” 插值方法,
分位數值就是這兩個數據點的平均值,即 (11+22)÷2=16.5
print(arrs.quantile(0.25, interpolation=“midpoint”))
describe() 常見統計信息
print(arrs.describe())
value_counts() 每個元素的個數
print(arrs.value_counts())
count() 非缺失值元素的個數
print(arrs.count())
print(len(arrs))
drop_duplicates() 去重 這里可以看出,底層None也作為NaN處理
print(arrs.drop_duplicates())
unique() 去重后的數組
print(arrs.unique())
nunique() 去重后非缺失值元素元素個數
print(arrs.nunique())
sample() 隨機采樣
print(arrs.sample())
sort_index() 按索引排序
print(arrs.sort_index())
sort_values() 按值排序
print(arrs.sort_values())
replace() 用指定值代替原有值
print(arrs.replace(22,“haha”))
to_frame() 將Series轉換為DataFrame
print(arrs.to_frame())
equals() 判斷兩個Series是否相同
arr1 = pd.Series([1,2,3])
arr2 = pd.Series([1,2,3])
print(arr1.equals(arr2))
keys() 返回Series的索引對象
print(arrs.index)
print(arrs.keys())
corr() 計算與另一個Series的相關系數
arr1.corr(arr2):由于 arr1 和 arr2 的值完全相同,它們之間是完全正相關的,
#因此相關系數為 1。
arr1.corr(arr3):arr1 的值是遞增的,而 arr3 的值是遞減的,它們之間是完全
負相關的,所以相關系數為 -1。
arr1.corr(arr4):arr1 和 arr4 的值都是遞增的,且變化趨勢一致,它們之間是
完全正相關的,相關系數為 1。
arr5.corr(arr6):arr5 和 arr6 的值之間沒有明顯的線性關系,它們的相關系數
為 0。
arr3 = pd.Series([3,2,1])
arr4 = pd.Series([6,7,8])
arr5 = pd.Series([1, -1, 1, -1])
arr6 = pd.Series([1, 1, -1, -1])
print(arr1.corr(arr2))
print(arr1.corr(arr3))
print(arr1.corr(arr4))
print(arr5.corr(arr6))
cov() 計算與另一個Series的協方差
協方差用于衡量兩個變量的總體誤差,其值的正負表示兩個變量的變化方向關系:
正值表示同向變化,負值表示反向變化。
print(arr1.cov(arr3))
hist() 繪制直方圖
arr7 = pd.Series([3,2,1,1,1,2,2])
繪制直方圖
直方圖(Histogram)是一種用于展示數據分布的統計圖表,它通過將數據劃分為
若干個連續的區間( bins ),統計每個區間內數據的頻數或頻率,并用矩形條的
高度表示該區間的數值分布情況
arr7.hist(bins=3)
items() 獲取索引名以及值
for i,v in arr7.items():
print(i,v)
3.2.5 Series的布爾索引
可以使用布爾索引從Series中篩選滿足某些條件的值。
s = pd.Series({“a”: -1.2, “b”: 3.5, “c”: 6.8, “d”: 2.9})
bools = s > s.mean() # 將大于平均值的元素標記為 True
print(bools)
a False
b True
c True
d False
dtype: bool
print(s[bools])
b 3.5
c 6.8
dtype: float64
3.2.6 Series的運算
1)Series與標量運算
標量會與每個元素進行計算。
s = pd.Series({“a”: -1.2, “b”: 3.5, “c”: 6.8, “d”: 2.9})
print(s * 10)
a -12.0
b 35.0
c 68.0
d 29.0
dtype: float64
2)Series與Series運算
會根據標簽索引進行對位計算,索引沒有匹配上的會用NaN填充。
s1 = pd.Series([1, 1, 1, 1])
s2 = pd.Series([2, 2, 2, 2], index=[1, 2, 3, 4])
print(s1 + s2)
0 NaN
1 3.0
2 3.0
3 3.0
4 NaN
dtype: float64
3.3 Pandas數據結構-DataFrame
DataFrame是Pandas 中的另一個核心數據結構,類似于一個二維的表格或數據庫中的數據表。它是一個表格型的數據結構,它含有一組有序的列,每列可以是不同的值類型(數值、字符串、布爾型值),既有行索引也有列索引。
DataFrame中的數據是以一個或多個二維塊存放的(而不是列表、字典或別的一維數據結構)。它可以被看做由Series組成的字典(共同用一個索引)。提供了各種功能來進行數據訪問、篩選、分割、合并、重塑、聚合以及轉換等操作,廣泛用于數據分析、清洗、轉換、可視化等任務。
3.3.1 DataFrame的創建
1)直接通過字典創建DataFrame
df = pd.DataFrame({“id”: [101, 102, 103], “name”: [“張三”, “李四”, “王五”], “age”: [20, 30, 40]})
print(df)
id name age
0 101 張三 20
1 102 李四 30
2 103 王五 40
2)通過字典創建時指定列的順序和行索引
需要安裝hypothesis 、pytest
df = pd.DataFrame(
data={“age”: [20, 30, 40], “name”: [“張三”, “李四”, “王五”]}, columns=[“name”, “age”], index=[101, 102, 103]
)
print(df)
name age
101 張三 20
102 李四 30
103 王五 40
3.3.2 DataFrame的常用屬性
屬性 說明
index DataFrame的行索引
columns DataFrame的列標簽
values DataFrame的值
ndim DataFrame的維度
shape DataFrame的形狀
size DataFrame的元素個數
dtypes DataFrame的元素類型
T 行列轉置
loc[] 顯式索引,按行列標簽索引或切片
iloc[] 隱式索引,按行列位置索引或切片
at[] 使用行列標簽訪問單個元素
iat[] 使用行列位置訪問單個元素
import pandas as pd
df = pd.DataFrame(data={“id”: [101, 102, 103], “name”: [“張三”, “李四”, “王五”], “age”: [20, 30, 40]},index=[“aa”, “bb”, “cc”])
index DataFrame的行索引
print(df.index)
columns DataFrame的列標簽
print(df.columns)
values DataFrame的值
print(df.values)
ndim DataFrame的維度
print(df.ndim)
shape DataFrame的形狀
print(df.shape)
size DataFrame的元素個數
print(df.size)
dtypes DataFrame的元素類型
print(df.dtypes)
T 行列轉置
print(df.T)
loc[] 顯式索引,按行列標簽索引或切片 逗號前是行切片規則,后是列切片規則
print(df.loc[“aa”:“cc”])
print(df.loc[:,[“id”,“name”]])
iloc[] 隱式索引,按行列位置索引或切片
print(df.iloc[0:1])
print(df.iloc[0:3,2])
print(“----------”)
at[] 使用行列標簽訪問單個元素
print(df.at[“aa”,“name”])
iat[] 使用行列位置訪問單個元素
print(df.iat[0,1])
3.3.3 DataFrame的常用方法
方法 說明
head() 查看前n行數據,默認5行
tail() 查看后n行數據,默認5行
isin() 元素是否包含在參數集合中
isna() 元素是否為缺失值
sum() 求和
mean() 平均值
min() 最小值
max() 最大值
var() 方差
std() 標準差
median() 中位數
mode() 眾數
quantile() 指定位置的分位數,如quantile(0.5)
describe() 常見統計信息
info() 基本信息
value_counts() 每個元素的個數
count() 非空元素的個數
drop_duplicates() 去重
sample() 隨機采樣
replace() 用指定值代替原有值
equals() 判斷兩個DataFrame是否相同
cummax() 累計最大值
cummin() 累計最小值
cumsum() 累計和
cumprod() 累計積
diff() 一階差分,對序列中的元素進行差分運算,也就是用當前元素減去前一個元素得到差值,默認情況下,它會計算一階差分,即相鄰元素之間的差值。參數:
periods:整數,默認為 1。表示要向前或向后移動的周期數,用于計算差值。正數表示向前移動,負數表示向后移動。
axis:指定計算的軸方向。0 或 ‘index’ 表示按列計算,1 或 ‘columns’ 表示按行計算,默認值為 0。
sort_index() 按行索引排序
sort_values() 按某列的值排序,可傳入列表來按多列排序,并通過ascending參數設置升序或降序
nlargest() 返回某列最大的n條數據
nsmallest() 返回某列最小的n條數據
在Pandas的 DataFrame 方法里,axis 是一個非常重要的參數,它用于指定操作的方向。
axis 參數可以取兩個主要的值,即 0 或 ‘index’,以及 1 或 ‘columns’ ,其含義如下:
? axis=0 或 axis=‘index’:表示操作沿著行的方向進行,也就是對每一列的數據進行處理。例如,當計算每列的均值時,就是對每列中的所有行數據進行計算。
? axis=1 或 axis=‘columns’:表示操作沿著列的方向進行,也就是對每行的數據進行處理。例如,當計算每行的總和時,就是對每行中的所有列數據進行計算。
import pandas as pd
df = pd.DataFrame(data={“id”: [101, 102, 103,104,105,106,101], “name”: [“張三”, “李四”, “王五”,“趙六”,“馮七”,“周八”,“張三”], “age”: [10, 20, 30, 40, None, 60,10]},index=[“aa”, “bb”, “cc”, “dd”, “ee”, “ff”,“aa”])
head() 查看前n行數據,默認5行
print(df.head())
tail() 查看后n行數據,默認5行
print(df.tail())
isin() 元素是否包含在參數集合中
print(df.isin([103,106]))
isna() 元素是否為缺失值
print(df.isna())
sum() 求和
print(df[“age”].sum())
mean() 平均值
print(df[“age”].mean())
min() 最小值
print(df[“age”].min())
max() 最大值
print(df[“age”].max())
var() 方差
print(df[“age”].var())
std() 標準差
print(df[“age”].std())
median() 中位數
print(df[“age”].median())
mode() 眾數
print(df[“age”].mode())
quantile() 指定位置的分位數,如quantile(0.5)
print(df[“age”].quantile(0.5))
describe() 常見統計信息
print(df.describe())
info() 基本信息
print(df.info())
value_counts() 每個元素的個數
print(df.value_counts())
count() 非空元素的個數
print(df.count())
drop_duplicates() 去重 duplicated()判斷是否為重復行
print(df.duplicated(subset=“age”))
sample() 隨機采樣
print(df.sample())
replace() 用指定值代替原有值
print(“----------------”)
print(df.replace(20,“haha”))
equals() 判斷兩個DataFrame是否相同
df1 = pd.DataFrame(data={“id”: [101, 102, 103], “name”: [“張三”, “李四”, “王五”], “age”: [10, 20, 30]})
df2 = pd.DataFrame(data={“id”: [101, 102, 103], “name”: [“張三”, “李四”, “王五”], “age”: [10, 20, 30]})
print(df1.equals(df2))
cummax() 累計最大值
df3 = pd.DataFrame({‘A’: [2, 5, 3, 7, 4],‘B’: [1, 6, 2, 8, 3]})
按列 等價于axis=0 默認
print(df3.cummax(axis=“index”))
按行 等價于axis=1
print(df3.cummax(axis=“columns”))
cummin() 累計最小值
print(df3.cummin())
cumsum() 累計和
print(df3.cumsum())
cumprod() 累計積
print(df3.cumprod())
diff() 一階差分
print(df3.diff())
sort_index() 按行索引排序
print(df.sort_index())
sort_values() 按某列的值排序,可傳入列表來按多列排序,并通過ascending參數設置升序或降序
print(df.sort_values(by=“age”))
nlargest() 返回某列最大的n條數據
print(df.nlargest(n=2,columns=“age”))
nsmallest() 返回某列最小的n條數據
print(df.nsmallest(n=1,columns=“age”))
3.3.4 DataFrame的布爾索引
可以使用布爾索引從DataFrame中篩選滿足某些條件的行。
df = pd.DataFrame(
data={“age”: [20, 30, 40, 10], “name”: [“張三”, “李四”, “王五”, “趙六”]},
columns=[“name”, “age”],
index=[101, 104, 103, 102],
)
print(df[“age”] > 25)
print(df[df[“age”] > 25])
#101 False
#104 True
#103 True
#102 False
Name: age, dtype: bool
name age
104 李四 30
103 王五 40
3.3.5 DataFrame的運算
1)DataFrame與標量運算
標量與每個元素進行計算。
df = pd.DataFrame(
data={“age”: [20, 30, 40, 10], “name”: [“張三”, “李四”, “王五”, “趙六”]},
columns=[“name”, “age”],
index=[101, 104, 103, 102],
)
print(df * 2)
name age
101 張三張三 40
104 李四李四 60
103 王五王五 80
102 趙六趙六 20
2)DataFrame與DataFrame運算
根據標簽索引進行對位計算,索引沒有匹配上的用NaN填充。
df1 = pd.DataFrame(
data={“age”: [10, 20, 30, 40], “name”: [“張三”, “李四”, “王五”, “趙六”]},
columns=[“name”, “age”],
index=[101, 102, 103, 104],
)
df2 = pd.DataFrame(
data={“age”: [10, 20, 30, 40], “name”: [“張三”, “李四”, “王五”, “田七”]},
columns=[“name”, “age”],
index=[102, 103, 104, 105],
)
print(df1 + df2)
name age
101 NaN NaN
102 李四張三 30.0
103 王五李四 50.0
104 趙六王五 70.0
105 NaN NaN
3.3.6 DataFrame的更改操作
1)設置行索引
創建DataFrame時如果不指定行索引,pandas會自動添加從0開始的索引。
df = pd.DataFrame({“age”: [20, 30, 40, 10], “name”: [“張三”, “李四”, “王五”, “趙六”], “id”: [101, 102, 103, 104]})
print(df)
age name id
0 20 張三 101
1 30 李四 102
2 40 王五 103
3 10 趙六 104
(1)通過set_index()設置行索引
inplace=True:這是一個布爾類型的參數。當設為 True 時,會直接在原
DataFrame上進行修改;若設為 False(默認值),則會返回一個新的
DataFrame,原DataFrame 保持不變
df.set_index(“id”, inplace=True) # 設置行索引
print(df)
age name
id
101 20 張三
102 30 李四
103 40 王五
104 10 趙六
(2)通過reset_index()重置行索引
df.reset_index(inplace=True) # 重置索引
print(df)
id age name
0 101 20 張三
1 102 30 李四
2 103 40 王五
3 104 10 趙六
2)修改行索引名和列名
(1)通過rename()修改行索引名和列名
df = pd.DataFrame({“age”: [20, 30, 40, 10], “name”: [“張三”, “李四”, “王五”, “趙六”], “id”: [101, 102, 103, 104]})
df.set_index(“id”, inplace=True)
print(df)
age name
id
101 20 張三
102 30 李四
103 40 王五
104 10 趙六
df.rename(index={101: “一”, 102: “二”, 103: “三”, 104: “四”}, columns={“age”: “年齡”, “name”: “姓名”}, inplace=True)
print(df)
年齡 姓名
id
一 20 張三
二 30 李四
三 40 王五
四 10 趙六
(2)將index和columns重新賦值
df.index = [“Ⅰ”, “Ⅱ”, “Ⅲ”, “Ⅳ”]
df.columns = [“年齡”, “名稱”]
print(df)
年齡 名稱
Ⅰ 20 張三
Ⅱ 30 李四
Ⅲ 40 王五
Ⅳ 10 趙六
3)添加列
通過 df[“列名”] 添加列。
df[“phone”] = [“13333333333”, “14444444444”, “15555555555”, “16666666666”]
print(df)
age name phone
id
101 20 張三 13333333333
102 30 李四 14444444444
103 40 王五 15555555555
104 10 趙六 16666666666
4)刪除列
(1)通過 df.drop(“列名”, axis=1) 刪除,也可是刪除行 axis=0
df.drop(“phone”, axis=1, inplace=True) # 刪除phone,按列刪除,inplace=True表示直接在原對象上修改
print(df)
age name
id
101 20 張三
102 30 李四
103 40 王五
104 10 趙六
(2)通過 del df[“列名”] 刪除
del df[“phone”]
print(df)
age name
id
101 20 張三
102 30 李四
103 40 王五
104 10 趙六
5)插入列
通過 insert(loc, column, value) 插入。該方法沒有inplace參數,直接在原數據上修改。
df.insert(loc=0, column=“phone”, value=df[“age”] * df.index)
print(df)
phone age name
id
101 2020 20 張三
102 3060 30 李四
103 4120 40 王五
104 1040 10 趙六
3.3.7 DataFrame數據的導入與導出
1)導出數據
方法 說明
to_csv() 將數據保存為csv格式文件,數據之間以逗號分隔,可通過sep參數設置使用其他分隔符,可通過index參數設置是否保存行標簽,可通過header參數設置是否保存列標簽。
to_pickle() 如要保存的對象是計算的中間結果,或者保存的對象以后會在Python中復用,可把對象保存為.pickle文件。如果保存成pickle文件,只能在python中使用。文件的擴展名可以是.p、.pkl、.pickle。
to_excel() 保存為Excel文件,需安裝openpyxl包。
to_clipboard() 保存到剪切板。
to_dict() 保存為字典。
to_hdf() 保存為HDF格式,需安裝tables包。
to_html() 保存為HTML格式,需安裝lxml、html5lib、beautifulsoup4包。
to_json() 保存為JSON格式。
to_feather() feather是一種文件格式,用于存儲二進制對象。feather對象也可以加載到R語言中使用。feather格式的主要優點是在Python和R語言之間的讀寫速度要比csv文件快。feather數據格式通常只用中間數據格式,用于Python和R之間傳遞數據,一般不用做保存最終數據。需安裝pyarrow包。
to_sql() 保存到數據庫。
import os
import pandas as pd
os.makedirs(“data”, exist_ok=True)
df = pd.DataFrame({“age”: [20, 30, 40, 10], “name”: [“張三”, “李四”, “王五”, “趙六”], “id”: [101, 102, 103, 104]})
df.set_index(“id”, inplace=True)
df.to_csv(“data/df.csv”)
df.to_csv(“data/df.tsv”, sep=“\t”) # 設置分隔符為 \t
df.to_csv(“data/df_noindex.csv”, index=False) # index=False 不保存行索引
df.to_pickle(“data/df.pkl”)
df.to_excel(“data/df.xlsx”)
df.to_clipboard()
df_dict = df.to_dict()
df.to_hdf(“data/df.h5”, key=“df”)
df.to_html(“data/df.html”)
df.to_json(“data/df.json”)
df.to_feather(“data/df.feather”)
2)導入數據
方法 說明
read_csv() 加載csv格式的數據。可通過sep參數指定分隔符,可通過index_col參數指定行索引。
read_pickle() 加載pickle格式的數據。
read_excel() 加載Excel格式的數據。
read_clipboard() 加載剪切板中的數據。
read_hdf() 加載HDF格式的數據。
read_html() 加載HTML格式的數據。
read_json() 加載JSON格式的數據。
read_feather() 加載feather格式的數據。
read_sql() 加載數據庫中的數據。
df_csv = pd.read_csv(“data/df.csv”, index_col=“id”) # 指定行索引
df_tsv = pd.read_csv(“data/df.tsv”, sep=“\t”) # 指定分隔符
df_pkl = pd.read_pickle(“data/df.pkl”)
df_excel = pd.read_excel(“data/df.xlsx”, index_col=“id”)
df_clipboard = pd.read_clipboard(index_col=“id”)
df_from_dict = pd.DataFrame(df_dict)
df_hdf = pd.read_hdf(“data/df.h5”, key=“df”)
df_html = pd.read_html(“data/df.html”, index_col=0)[0]
df_json = pd.read_json(“data/df.json”)
df_feather = pd.read_feather(“data/df.feather”)
print(df_csv)
print(df_tsv)
print(df_pkl)
print(df_excel)
print(df_clipboard)
print(df_from_dict)
print(df_hdf)
print(df_html)
print(df_json)
print(df_feather)
3.4 Pandas日期數據處理初識
3.4.1 to_datetime()進行日期格式轉換
1)參數說明
參數名 說明
arg 要轉換為日期時間的對象
errors ignore,raise,coerce, 默認為ignore,表示無效的解析將會返回原值
dayfirst 指定日期解析順序。如果為True,則以日期開頭解析日期,例如:“10/11/12”解析為2012-11-10。默認false
yearfirst 如果為True,則以日期開頭解析,例如:“10/11/12”解析為2010-11-12。如果dayfirst和yearfirst都為True,則yearfirst在前面。默認false。當日期字符串格式不明確時,指定年份是否在最前面。當日期字符串是 ‘2010/1/4’ 這種形式,由于年份是 4 位數字,pandas 能很清晰地識別出這是年份,所以即使 yearfirst 為 False,也不會影響其正確解析
utc 返回utc,即協調世界時間
format 格式化顯示時間的格式,字符串,默認值為None
exact 要求格式完全匹配
unit 參數的單位表示時間的單位
infer_datetime_format 如果為True且未給出格式,則嘗試基于第一個非nan元素推斷datetime字符串的格式,如果可以推斷,則切換到更快的解析方法。在某些情況下,這可以將解析速度提高5-10倍。
origin 默認值為unix,定義參考日期1970-01-01
cache 使用唯一的已轉換日期緩存來應用日期時間轉換。在解析重復日期字符串時產生顯著的加速。
2)將字符串字段轉換為日期類型
import pandas as pd
df = pd.DataFrame({“gmv”:[100,200,300,400],“trade_date”:[“2025-01-06”,“2023-10-31”,“2023-12-31”,“2023-01-05”]})
df[“ymd”] = pd.to_datetime(df[“trade_date”])
print(df)
3.4.2 時間屬性訪問器對象Series.dt,獲取日期數據的年月日星期
1)獲取年月日
df[‘yy’],df[‘mm’],df[‘dd’]=df[‘ymd’].dt.year,df[‘ymd’].dt.month,df[‘ymd’].dt.day
print(df)
2)獲取星期
df[‘week’]=df[‘ymd’].dt.day_name()
print(df)
3)獲取日期所在季度
df[‘quarter’]=df[‘ymd’].dt.quarter
print(df)
4)判斷日期是否月底年底
df[‘mend’]=df[‘ymd’].dt.is_month_end
df[‘yend’]=df[‘ymd’].dt.is_year_end
print(df)
3.4.3 to_period()獲取統計周期
freq:這是 to_period() 方法最重要的參數,用于指定要轉換的時間周期頻率
常見的取值如下:
? “D”:按天周期,例如 2024-01-01 會轉換為 2024-01-01 這個天的周期。
? “W”:按周周期,通常以周日作為一周的結束,比如日期落在某一周內,就會轉換為該周的周期表示。
? “M”:按月周期,像 2024-05-15 會轉換為 2024-05。
? “Q”:按季度周期,一年分為四個季度,日期會轉換到對應的季度周期,例如 2024Q2 。
? “A” 或 “Y”:按年周期,如 2024-07-20 會轉換為 2024 。
df[“ystat”] = df[“ymd”].dt.to_period(“Y”)
df[“mstat”] = df[“ymd”].dt.to_period(“M”)
df[“qstat”] = df[“ymd”].dt.to_period(“Q”)
df[“wstat”] = df[“ymd”].dt.to_period(“W”)
print(df)
3.5 DataFrame數據分析入門
3.5.1 加載數據集
使用weather(天氣)數據集。其中包含6個字段:
? date:日期,年-月-日格式。
? precipitation:降水量。
? temp_max:最高溫度。
? temp_min:最低溫度。
? wind:風力。
? weather:天氣狀況。
import pandas as pd
df = pd.read_csv(“data/weather.csv”)
print(type(df)) # 查看df類型
print(df.shape) # 查看df形狀
print(df.columns) # 查看df的列名
print(df.dtypes) # 查看df各列數據類型
df.info() # 查看df基本信息
pandas與Python常用數據類型對照:
pandas類型 Python類型 說明
object string 字符串類型
int64 int 整型
float64 float 浮點型
datetime64 datetime 日期時間類型
3.5.2 查看部分數據
1)通過head()、tail()獲取前n行或后n行
print(df.head())
print(df.tail(10))
2)獲取一列或多列數據
(1)加載一列數據
df_date_series = df[“date”] # 返回的是Series
df_date_dataframe = df[[“date”]] # 返回的是DataFrame
(2)加載多列數據
df[[“date”, “temp_max”, “temp_min”]] # 獲取多列數據
3)按行獲取數據
(1)loc:通過行標簽獲取數據
df.loc[1] # 獲取行標簽為1的數據
df.loc[[1, 10, 100]] # 獲取行標簽分別為1、10、100的數據
(2)iloc:通過行位置獲取數據
df.iloc[0] # 獲取行位置為0的數據
df.iloc[-1] # 獲取行位置為最后一位的數據
4)獲取指定行與列的數據
df.loc[1, “precipitation”] # 獲取行標簽為1,列標簽為precipitation的數據
df.loc[:, “precipitation”] # 獲取所有行,列標簽為precipitation的數據
df.iloc[:, [3, 5, -1]] # 獲取所有行,列位置為3,5,最后一位的數據
df.iloc[:10, 2:6] # 獲取前10行,列位置為2、3、4、5的數據
df.loc[:10, [“date”, “precipitation”, “temp_max”, “temp_min”]] # 通過行列標簽獲取數據
3.5.3 分組聚合計算
df.groupby(“分組字段”)[“要聚合的字段”].聚合函數()
df.groupby([“分組字段”, “分組字段2”, …])[[“要聚合的字段”, “要聚合的字段2”, …]].聚合函數()
(1)將數據按月分組,并統計最大溫度和最小溫度的平均值
df[“month”] = pd.to_datetime(df[“date”]).dt.to_period(“M”).astype(str) # 將date轉換為 年-月 的格式
df_groupby_date = df.groupby(“month”) # 按month分組,返回一個分組對象(DataFrameGroupBy)
month_temp = df_groupby_date[[“temp_max”, “temp_min”]] # 從分組對象中選擇特定的列
month_temp_mean = month_temp.mean() # 對每個列求平均值
以上代碼可以寫在一起
month_temp_mean = df.groupby(“month”)[[“temp_max”, “temp_min”]].mean()
temp_max temp_min
month
2012-01 7.054839 1.541935
2012-02 9.275862 3.203448
2012-03 9.554839 2.838710
2012-04 14.873333 5.993333
2012-05 17.661290 8.190323
分組后默認會將分組字段作為行索引。如果分組字段有多個,得到的是復合索引。
(2)分組頻數計算
統計每個月不同天氣狀況的數量。
df.groupby(“month”)[“weather”].nunique()
date
2012-01 4
2012-02 4
2012-03 4
2012-04 4
2012-05 3
3.5.4 基本繪圖
plot():pandas 提供的繪圖方法,它基于 matplotlib 庫。將前面計算得到的均值結果繪制成圖表,默認情況下會繪制折線圖,其中 “month” 作為 x 軸,“temp_max” 和 “temp_min” 的均值作為 y 軸。
df.groupby(“month”)[[“temp_max”, “temp_min”]].mean().plot() # 使用plot繪制折線圖
3.5.5 常用統計值
可通過describe()查看常用統計信息。
df.describe() # 查看常用統計信息
df.describe().T # 行列轉置
可通過include參數指定要統計哪些數據類型的列。
df.describe(include=“all”) # 統計所有列
df.describe(include=[“float64”]) # 只統計數據類型為float64的列
3.5.6 常用排序方法
nlargest(n, [列名1, 列名2, …]):按列排序的最大n個
nsmallest(n, [列名1, 列名2, …]):按列排序的最小n個
sort_values([列名1, 列名2, …], asceding=[True, False, …]):按列升序或降序排序
drop_duplicates(subset=[列名1, 列名2]):按列去重
(1)找到最高溫度最大的30天
通過nlargest()找出temp_max最大的30條數據。
df = pd.read_csv(“data/weather.csv”)
df.nlargest(30, “temp_max”)
(2)從最高溫度最大的30天中找出最低溫度最小的5天
通過nlargest()找出temp_min最小的5條數據。
df.nlargest(30, “temp_max”).nsmallest(5, “temp_min”)
(3)找出每年的最高溫度
df[“year”] = pd.to_datetime(df[“date”]).dt.to_period(“Y”).astype(str) # 將date轉換為 年 格式
df_sort = df.sort_values([“year”, “temp_max”], ascending=[True, False]) # 按year升序,temp_max降序排序
df_sort.drop_duplicates(subset=“year”) # 按year去重
date precipitation temp_max temp_min wind weather year
228 2012-08-16 0.0 34.4 18.3 2.8 sun 2012
546 2013-06-30 0.0 33.9 17.2 2.5 sun 2013
953 2014-08-11 0.5 35.6 17.8 2.6 rain 2014
1295 2015-07-19 0.0 35.0 17.2 3.3 sun 2015
3.5.7 案例:簡單數據分析練習
使用employees(員工)數據集,其中包含10個字段:
? employee_id:員工id。
? first_name:員工名稱。
? last_name:員工姓氏。
? email:員工郵箱。
? phone_number:員工電話號碼。
? job_id:員工工種。
? salary:員工薪資。
? commission_pct:員工傭金比例。
? manager_id:員工領導的id。
? department_id:員工的部門id。
1)加載數據
import pandas as pd
df = pd.read_csv(“data/employees.csv”) # 加載員工數據
2)查看數據
print(df.head()) # 查看前5行
df.info() # 查看數據信息
print(df.describe()) # 查看統計信息
print(df.shape) # 查看數據形狀
3)找出薪資最低、最高的員工
print(df[df[“salary”] == df[“salary”].min()]) # 找出最低薪資的員工
print(df.loc[df[“salary”] == df[“salary”].min()]) # 找出最低薪資的員工
print(df.loc[df[“salary”] == df[“salary”].max()]) # 找出最高薪資的員工
print(df.sort_values(“salary”).head(1)) # 使用排序的方法找出最低薪資的員工
print(df.sort_values(“salary”, ascending=False).head(1)) # 使用排序的方法找出最高薪資的員工
4)找出薪資最高的10名員工
print(df.nlargest(10, “salary”)) # 薪資最高的10名員工
5)查看所有部門id
print(df[“department_id”].unique()) # 所有部門id
6)查看每個部門的員工數
print(df.groupby(“department_id”)[“employee_id”].count().rename(“employee_count”)) # 查看每個部門的員工數
7)繪圖
df.groupby(“department_id”)[“employee_id”].count().rename(“employee_count”).plot(kind=“bar”)
8)薪資的分布
print(df[“salary”].mean()) # 平均值
print(df[“salary”].std()) # 標準差
print(df[“salary”].median()) # 中位數
9)找出平均薪資最高的部門id
print(df.groupby(“department_id”)[“salary”].mean().nlargest(1)) # 平均薪資最高的部門
3.6 Padas的數據組合函數
3.6.1 concat連接
沿著一條軸將多個對象堆疊到一起,可通過axis參數設置沿哪一條軸連接。
1)Series與Series連接
s1 = pd.Series([“A”, “B”], index=[1, 2])
s2 = pd.Series([“D”, “E”], index=[4, 5])
s3 = pd.Series([“G”, “H”], index=[7, 8])
pd.concat([s1, s2, s3]) # 按行連接
1 A
2 B
4 D
5 E
7 G
8 H
dtype: object
pd.concat([s1, s2, s3], axis=1) # 按列連接
0 1 2
1 A NaN NaN
2 B NaN NaN
4 NaN D NaN
5 NaN E NaN
7 NaN NaN G
8 NaN NaN H
缺失值會用NaN填充。
2)DataFrame與Series連接
df1 = pd.DataFrame(data={“a”: [1, 2], “b”: [4, 5]}, index=[1, 2])
s1 = pd.Series(data=[7, 10], index=[1, 2], name=“a”)
pd.concat([df1, s1]) # 按行連接
a b
1 1 4.0
2 2 5.0
1 7 NaN
2 10 NaN
pd.concat([df1, s1], axis=1) # 按列連接
a b a
1 1 4 7
2 2 5 10
3)DataFrame與DataFrame連接
df1 = pd.DataFrame(data={“a”: [1, 2], “b”: [4, 5]}, index=[1, 2])
df2 = pd.DataFrame(data={“a”: [7, 8], “b”: [10, 11]}, index=[1, 2])
pd.concat([df1, df2]) # 按行連接
a b
1 1 4
2 2 5
1 7 10
2 8 11
pd.concat([df1, df2], axis=1) # 按列連接
a b a b
1 1 4 7 10
2 2 5 8 11
4)重置索引
可通過ignore_index=True來重置索引。
df1 = pd.DataFrame(data={“a”: [1, 2], “b”: [4, 5]}, index=[1, 2])
df2 = pd.DataFrame(data={“a”: [7, 8], “b”: [10, 11]}, index=[1, 2])
pd.concat([df1, df2], ignore_index=True) # 重置索引
a b
0 1 4
1 2 5
2 7 10
3 8 11
5)類似join的連接
默認的合并方式是對其他軸進行并集合并(join=outer),可以用join=inner實現其他軸上的交集合并。
df1 = pd.DataFrame(data={“a”: [1, 2], “b”: [4, 5]}, index=[1, 2])
df2 = pd.DataFrame(data={“b”: [7, 8], “c”: [10, 11]}, index=[2, 3])
pd.concat([df1, df2])
a b c
1 1.0 4 NaN
2 2.0 5 NaN
2 NaN 7 10.0
3 NaN 8 11.0
pd.concat([df1, df2], join=“inner”)
b
1 4
2 5
2 7
3 8
3.6.2 merge合并
通過一個或多個列將行連接。
1)數據連接的類型
merge()實現了三種數據連接的類型:一對一、多對一和多對多。
(1)一對一連接
df1 = pd.DataFrame(
{“employee”: [“Bob”, “Jake”, “Lisa”, “Sue”], “group”: [“Accounting”, “Engineering”, “Engineering”, “HR”]}
)
df2 = pd.DataFrame({“employee”: [“Lisa”, “Bob”, “Jake”, “Sue”], “hire_date”: [2004, 2008, 2012, 2014]})
print(df1)
employee group
0 Bob Accounting
1 Jake Engineering
2 Lisa Engineering
3 Sue HR
print(df2)
employee hire_date
0 Lisa 2004
1 Bob 2008
2 Jake 2012
3 Sue 2014
通過相同的字段名employee進行關聯的
df3 = pd.merge(df1, df2)
print(df3)
employee group hire_date
0 Bob Accounting 2008
1 Jake Engineering 2012
2 Lisa Engineering 2004
3 Sue HR 2014
(2)多對一連接
在需要連接的兩個列中,有一列的值有重復。通過多對一連接獲得的結果將會保留重復值。
df1 = pd.DataFrame(
{“employee”: [“Bob”, “Jake”, “Lisa”, “Sue”], “group”: [“Accounting”, “Engineering”, “Engineering”, “HR”]}
)
df2 = pd.DataFrame({“group”: [“Accounting”, “Engineering”, “HR”], “supervisor”: [“Carly”, “Guido”, “Steve”]})
print(df1)
employee group
0 Bob Accounting
1 Jake Engineering
2 Lisa Engineering
3 Sue HR
print(df2)
group supervisor
0 Accounting Carly
1 Engineering Guido
2 HR Steve
df3 = pd.merge(df1, df2)
print(df3)
employee group supervisor
0 Bob Accounting Carly
1 Jake Engineering Guido
2 Lisa Engineering Guido
3 Sue HR Steve
在supervisor列中有些值會因為輸入數據的對應關系而有所重復。
(3)多對多連接
如果左右兩個輸入的共同列都包含重復值,那么合并的結果就是一種多對多連接。
df1 = pd.DataFrame(
{“employee”: [“Bob”, “Jake”, “Lisa”, “Sue”], “group”: [“Accounting”, “Engineering”, “Engineering”, “HR”]}
)
df2 = pd.DataFrame(
{
“group”: [“Accounting”, “Accounting”, “Engineering”, “Engineering”, “HR”, “HR”],
“skills”: [“math”, “spreadsheets”, “coding”, “linux”, “spreadsheets”, “organization”],
}
)
print(df1)
employee group
0 Bob Accounting
1 Jake Engineering
2 Lisa Engineering
3 Sue HR
print(df2)
group skills
0 Accounting math
1 Accounting spreadsheets
2 Engineering coding
3 Engineering linux
4 HR spreadsheets
5 HR organization
df3 = pd.merge(df1, df2)
print(df3)
employee group skills
0 Bob Accounting math
1 Bob Accounting spreadsheets
2 Jake Engineering coding
3 Jake Engineering linux
4 Lisa Engineering coding
5 Lisa Engineering linux
6 Sue HR spreadsheets
7 Sue HR organization
多對多連接產生的是行的笛卡爾積。由于左邊有2個Engineering,右邊有2個Engineering,所以最終結果有4個Engineering。
2)設置合并的鍵與索引
merge()會將兩個輸入的一個或多個共同列作為鍵進行合并。但由于兩個輸入要合并的列通常都不是同名的,因此merge()提供了一些參數處理這個問題。
(1)通過on指定使用某個列連接,只能在有共同列名的時候使用
df1 = pd.DataFrame(
{“employee”: [“Bob”, “Jake”, “Lisa”, “Sue”], “group”: [“Accounting”, “Engineering”, “Engineering”, “HR”]}
)
df2 = pd.DataFrame({“employee”: [“Lisa”, “Bob”, “Jake”, “Sue”], “hire_date”: [2004, 2008, 2012, 2014]})
print(df1)
employee group
0 Bob Accounting
1 Jake Engineering
2 Lisa Engineering
3 Sue HR
print(df2)
employee hire_date
0 Lisa 2004
1 Bob 2008
2 Jake 2012
3 Sue 2014
df3 = pd.merge(df1, df2, on=“employee”)
print(df3)
employee group hire_date
0 Bob Accounting 2008
1 Jake Engineering 2012
2 Lisa Engineering 2004
3 Sue HR 2014
(2)兩對象列名不同,通過left_on和right_on分別指定列名
df1 = pd.DataFrame(
{“employee”: [“Bob”, “Jake”, “Lisa”, “Sue”], “group”: [“Accounting”, “Engineering”, “Engineering”, “HR”]}
)
df2 = pd.DataFrame({“name”: [“Bob”, “Jake”, “Lisa”, “Sue”], “salary”: [70000, 80000, 120000, 90000]})
print(df1)
employee group
0 Bob Accounting
1 Jake Engineering
2 Lisa Engineering
3 Sue HR
print(df2)
name salary
0 Bob 70000
1 Jake 80000
2 Lisa 120000
3 Sue 90000
df3 = pd.merge(df1, df2, left_on=“employee”, right_on=“name”)
print(df3)
employee group name salary
0 Bob Accounting Bob 70000
1 Jake Engineering Jake 80000
2 Lisa Engineering Lisa 120000
3 Sue HR Sue 90000
(3)通過left_index和right_index設置合并的索引
通過設置merge()中的left_index、right_index參數將索引設置為鍵來實現合并。
df1 = pd.DataFrame(
{“employee”: [“Bob”, “Jake”, “Lisa”, “Sue”], “group”: [“Accounting”, “Engineering”, “Engineering”, “HR”]}
)
df2 = pd.DataFrame({“employee”: [“Lisa”, “Bob”, “Jake”, “Sue”], “hire_date”: [2004, 2008, 2012, 2014]})
df1.set_index(“employee”, inplace=True)
df2.set_index(“employee”, inplace=True)
print(df1)
group
employee
Bob Accounting
Jake Engineering
Lisa Engineering
Sue HR
print(df2)
hire_date
employee
Lisa 2004
Bob 2008
Jake 2012
Sue 2014
設置索引后,如果不指定關聯列會報錯,建議通過以下方式指定,on="employee"也可#以實現,但是不同的解釋器可能效果不一樣,因為設置索引后,employee就不算是列了
df3 = pd.merge(df1, df2, left_index=True, right_index=True)
group hire_date
employee
Bob Accounting 2008
Jake Engineering 2012
Lisa Engineering 2004
Sue HR 2014
DataFrame實現了join()方法,可以按照索引進行數據合并。但要求沒有重疊的列,或通過lsuffix、rsuffix指定重疊列的后綴。
import pandas as pd
df1 = pd.DataFrame({
‘key’: [‘A’, ‘B’, ‘C’],
‘value1’: [1, 2, 3]
})
df2 = pd.DataFrame({
‘key’: [‘B’, ‘C’, ‘D’],
‘value2’: [4, 5, 6]
})
合并兩個 DataFrame,并處理列名沖突,如果通過on指定列,只是指定的df1。會用
df1指定的列和df2的索引連接,會報錯。除非df2.set_index指定索引。
df1.join(df2,lsuffix=‘_left’,rsuffix=‘_right’)
3)設置數據連接的集合操作規則
當一個值出現在一列,卻沒有出現在另一列時,就需要考慮集合操作規則了。
df1 = pd.DataFrame({“name”: [“Peter”, “Paul”, “Mary”], “food”: [“fish”, “beans”, “bread”]}, columns=[“name”, “food”])
df2 = pd.DataFrame({“name”: [“Mary”, “Joseph”], “drink”: [“wine”, “beer”]}, columns=[“name”, “drink”])
print(df1)
name food
0 Peter fish
1 Paul beans
2 Mary bread
print(df2)
name drink
0 Mary wine
1 Joseph beer
print(pd.merge(df1, df2))
name food drink
0 Mary bread wine
合并兩個數據集,在name列中只有一個共同的值Mary。默認情況下,結果中只會包含兩個輸入集合的交集,這種連接方式被稱為內連接(inner join)。
我們可以通過how參數設置連接方式,默認值為inner。how參數支持的數據連接方式還有outer、left和right。外連接(outer join)返回兩個輸入列的并集,所有缺失值都用 NaN 填充。
print(pd.merge(df1, df2, how=“outer”))
name food drink
0 Joseph NaN beer
1 Mary bread wine
2 Paul beans NaN
3 Peter fish NaN
左連接(left join)和右連接(right join)返回的結果分別只包含左列和右列。
print(pd.merge(df1, df2, how=“left”))
name food drink
0 Peter fish NaN
1 Paul beans NaN
2 Mary bread wine
4)重復列名的處理
可能會遇到兩個輸入DataFrame有重名列的情況,merge()會自動為其增加后綴_x和_y,也可以通過suffixes參數自定義后綴名。
df1 = pd.DataFrame({“name”: [“Bob”, “Jake”, “Lisa”, “Sue”], “rank”: [1, 2, 3, 4]})
df2 = pd.DataFrame({“name”: [“Bob”, “Jake”, “Lisa”, “Sue”], “rank”: [3, 1, 4, 2]})
print(df1)
name rank
0 Bob 1
1 Jake 2
2 Lisa 3
3 Sue 4
print(df2)
name rank
0 Bob 3
1 Jake 1
2 Lisa 4
3 Sue 2
print(pd.merge(df1, df2, on=“name”)) # 不指定后綴名,默認為_x和_y
name rank_x rank_y
0 Bob 1 3
1 Jake 2 1
2 Lisa 3 4
3 Sue 4 2
print(pd.merge(df1, df2, on=“name”, suffixes=(“_df1”, “_df2”))) # 通過suffixes指定后綴名
name rank_df1 rank_df2
0 Bob 1 3
1 Jake 2 1
2 Lisa 3 4
3 Sue 4 2
3.7 Padas的缺失值處理函數
3.7.1 pandas中的缺失值
pandas使用浮點值NaN(Not a Number)表示缺失數據,使用NA(Not Available)表示缺失值。可以通過isnull()、isna()或notnull()、notna()方法判斷某個值是否為缺失值。
Nan通常表示一個無效的或未定義的數字值,是浮點數的一種特殊取值,用于表示那些不能表示為正常數字的情況,如 0/0、∞-∞等數學運算的結果。nan與任何值(包括它自身)進行比較的結果都為False。例如在 Python 中,nan == nan返回False。
NA一般用于表示數據不可用或缺失的情況,它的含義更側重于數據在某種上下文中是缺失或不存在的,不一定特指數字類型的缺失。
na和nan都用于表示缺失值,但nan更強調是數值計算中的特殊值,而na更強調數據的可用性或存在性。
s = pd.Series([np.nan, None, pd.NA])
print(s)
0 NaN
1 None
2
dtype: object
print(s.isnull())
0 True
1 True
2 True
dtype: bool
3.7.2 加載數據中包含缺失值
df = pd.read_csv(“data/weather_withna.csv”)
print(df.tail(5))
date precipitation temp_max temp_min wind weather
1456 2015-12-27 NaN NaN NaN NaN NaN
1457 2015-12-28 NaN NaN NaN NaN NaN
1458 2015-12-29 NaN NaN NaN NaN NaN
1459 2015-12-30 NaN NaN NaN NaN NaN
1460 2015-12-31 20.6 12.2 5.0 3.8 rain
可以通過keep_default_na參數設置是否將空白值設置為缺失值。
df = pd.read_csv(“data/weather_withna.csv”, keep_default_na=False)
print(df.tail(5))
date precipitation temp_max temp_min wind weather
1456 2015-12-27
1457 2015-12-28
1458 2015-12-29
1459 2015-12-30
1460 2015-12-31 20.6 12.2 5.0 3.8 rain
可通過na_values參數將指定值設置為缺失值。
df = pd.read_csv(“data/weather_withna.csv”, na_values=[“2015-12-31”])
print(df.tail(5))
date precipitation temp_max temp_min wind weather
1456 2015-12-27 NaN NaN NaN NaN NaN
1457 2015-12-28 NaN NaN NaN NaN NaN
1458 2015-12-29 NaN NaN NaN NaN NaN
1459 2015-12-30 NaN NaN NaN NaN NaN
1460 NaN 20.6 12.2 5.0 3.8 rain
3.7.3 查看缺失值
1)通過isnull()查看缺失值數量
df = pd.read_csv(“data/weather_withna.csv”)
print(df.isnull().sum())
date 0
precipitation 303
temp_max 303
temp_min 303
wind 303
weather 303
dtype: int64
2)通過missingno條形圖展示缺失值
先安裝missingno包:pip install missingno
import missingno as msno
import pandas as pd
df = pd.read_csv(“data/weather_withna.csv”)
msno.bar(df)
3)通過熱力圖查看缺失值的相關性
missingno繪制的熱力圖能夠展示數據集中不同列的缺失值之間的相關性。這里的相關性體現的是當某一列出現缺失值時,其他列出現缺失值的可能性。如果兩個列的缺失值呈現正相關,意味著當其中一列有缺失值時,另一列也很可能有缺失值;若為負相關,則表示當一列有缺失值時,另一列更傾向于沒有缺失值。
? 顏色與數值:熱力圖中的顏色和數值反映了列之間缺失值的相關性。接近 1 表示正相關,接近 -1 表示負相關,接近 0 則表示缺失值之間沒有明顯的關聯。
? 示例說明:假如 A 列和 B 列在熱力圖中對應區域顏色較深且數值接近 1,這就表明當 A 列出現缺失值時,B 列也很可能出現缺失值;若數值接近 -1,情況則相反。
msno.heatmap(df)
3.7.4 剔除缺失值
通過dropna()方法來剔除缺失值。
1)Series剔除缺失值
s = pd.Series([1, pd.NA, None])
print(s)
0 1
1
2 None
dtype: object
print(s.dropna())
0 1
dtype: object
2)DataFrame剔除缺失值
無法從DataFrame中單獨剔除一個值,只能剔除缺失值所在的整行或整列。默認情況下,dropna()會剔除任何包含缺失值的整行數據。
df = pd.DataFrame([[1, pd.NA, 2], [2, 3, 5], [pd.NA, 4, 6]])
print(df)
0 1 2
0 1 2
1 2 3 5
2 4 6
print(df.dropna())
0 1 2
1 2 3 5
可以設置按不同的坐標軸剔除缺失值,比如axis=1(或 axis=‘columns’)會剔除任何包含缺失值的整列數據。
df = pd.DataFrame([[1, pd.NA, 2], [2, 3, 5], [pd.NA, 4, 6]])
print(df)
0 1 2
0 1 2
1 2 3 5
2 4 6
print(df.dropna(axis=1))
2
0 2
1 5
2 6
有時只需要剔除全部是缺失值的行或列,或者絕大多數是缺失值的行或列。這些需求可以通過設置how或thresh參數來滿足,它們可以設置剔除行或列缺失值的數量閾值。
df = pd.DataFrame([[1, pd.NA, 2], [pd.NA, pd.NA, 5], [pd.NA, pd.NA, pd.NA]])
print(df)
0 1 2
0 1 2
1 5
2
print(df.dropna(how=“all”)) # 如果所有值都是缺失值,則刪除這一行
0 1 2
0 1 2
1 5
print(df.dropna(thresh=2)) # 如果至少有2個值不是缺失值,則保留這一行
0 1 2
0 1 2
可以通過設置subset參數來設置某一列有缺失值則進行剔除。
df = pd.DataFrame([[1, pd.NA, 2], [pd.NA, pd.NA, 5], [pd.NA, pd.NA, pd.NA]])
print(df)
0 1 2
0 1 2
1 5
2
print(df.dropna(subset=[0])) # 如果0列有缺失值,則刪除這一行
0 1 2
0 1 2
3.7.5 填充缺失值
1)使用固定值填充
通過fillna()方法,傳入值或字典進行填充。
df = pd.read_csv(“data/weather_withna.csv”)
print(df.fillna(0).tail()) # 使用固定值填充
date precipitation temp_max temp_min wind weather
1456 2015-12-27 0.0 0.0 0.0 0.0 0
1457 2015-12-28 0.0 0.0 0.0 0.0 0
1458 2015-12-29 0.0 0.0 0.0 0.0 0
1459 2015-12-30 0.0 0.0 0.0 0.0 0
1460 2015-12-31 20.6 12.2 5.0 3.8 rain
print(df.fillna({“temp_max”: 60, “temp_min”: -60}).tail()) # 使用字典來填充
date precipitation temp_max temp_min wind weather
1456 2015-12-27 NaN 60.0 -60.0 NaN NaN
1457 2015-12-28 NaN 60.0 -60.0 NaN NaN
1458 2015-12-29 NaN 60.0 -60.0 NaN NaN
1459 2015-12-30 NaN 60.0 -60.0 NaN NaN
1460 2015-12-31 20.6 12.2 5.0 3.8 rain
2)使用統計值填充
通過fillna()方法,傳入統計后的值進行填充。
print(df.fillna(df[[“precipitation”, “temp_max”, “temp_min”, “wind”]].mean()).tail()) # 使用平均值填充
date precipitation temp_max temp_min wind weather
1456 2015-12-27 3.052332 15.851468 7.877202 3.242055 NaN
1457 2015-12-28 3.052332 15.851468 7.877202 3.242055 NaN
1458 2015-12-29 3.052332 15.851468 7.877202 3.242055 NaN
1459 2015-12-30 3.052332 15.851468 7.877202 3.242055 NaN
1460 2015-12-31 20.600000 12.200000 5.000000 3.800000 rain
3)使用前后的有效值填充
通過ffill()或bfill()方法使用前面或后面的有效值填充。
print(df.ffill().tail()) # 使用前面的有效值填充
date precipitation temp_max temp_min wind weather
1456 2015-12-27 0.0 11.1 4.4 4.8 sun
1457 2015-12-28 0.0 11.1 4.4 4.8 sun
1458 2015-12-29 0.0 11.1 4.4 4.8 sun
1459 2015-12-30 0.0 11.1 4.4 4.8 sun
1460 2015-12-31 20.6 12.2 5.0 3.8 rain
print(df.bfill().tail()) # 使用后面的有效值填充
date precipitation temp_max temp_min wind weather
1456 2015-12-27 20.6 12.2 5.0 3.8 rain
1457 2015-12-28 20.6 12.2 5.0 3.8 rain
1458 2015-12-29 20.6 12.2 5.0 3.8 rain
1459 2015-12-30 20.6 12.2 5.0 3.8 rain
1460 2015-12-31 20.6 12.2 5.0 3.8 rain
4)通過線性插值填充
通過interpolate()方法進行線性插值填充。線性插值操作,就是用于在已知數據點之間估算未知數據點的值。interpolate 方法支持多種插值方法,可通過 method 參數指定,常見的方法有:
? ‘linear’:線性插值,基于兩點之間的直線來估算缺失值,適用于數據呈線性變化的情況。
? ‘time’:適用于時間序列數據,會考慮時間間隔進行插值。
? ‘polynomial’:多項式插值,通過擬合多項式曲線來估算缺失值,可通過 order 參數指定多項式的階數。
import pandas as pd
import numpy as np
創建包含缺失值的 Series
s = pd.Series([1, np.nan, 3, 4, np.nan, 6])
使用默認的線性插值方法填充缺失值
s_interpolated = s.interpolate()
print(s_interpolated)
0 1.0
1 2.0
2 3.0
3 4.0
4 5.0
5 6.0
dtype: float64
3.8 Padas的apply函數
apply()函數可以對DataFrame或Series的數據進行逐行、逐列或逐元素的操作。可以使用自定義函數對數據進行變換、計算或處理,通常用于處理復雜的變換邏輯,或者處理不能通過向量化操作輕松完成的任務。
3.8.1 Series使用apply()
import pandas as pd
Series的apply方法調用的函數,參數接收的是Series中的一個元素
def func(item):
return item * 20
s = pd.Series([10, 20, 30])
s.apply(func)
也可以傳入lambda表達式。
s.apply(lambda item: item * 20)
傳入帶參數的函數。
apply()方法會將自己沒有匹配上的參數,在調用func的時候作為func的參數傳遞過去,必須通過關鍵字傳參
def func1(item,p1):
return item * p1
s.apply(func1,p1=3)
3.8.2 DataFrame使用apply()
DataFrame的apply方法調用的函數,參數接收的是DataFrame中的一個Series
def func(s):
return s.sum()
df = pd.DataFrame({“a”: [10, 20, 30], “b”: [40, 50, 60]})
df.apply(func)
#默認axis=0,按行方向進行操作,對列進行統計;
#可以設置axis=1,按照列的方向進行操作,對行進行統計
df.apply(func, axis=1)
注意:df.apply 一次只能處理一個 Series(當 axis=0 時處理列,當 axis=1 時處理行),但如下的函數 func中,獲取同一個Series對象的兩列數據a和b,所以不能直接使用 df.apply(f)按列處理了,需要指定axis=1,按行處理,才能獲取一行中的不同列。
def func(s):
return s[“a”] / s[“b”]
df = pd.DataFrame({“a”: [10, 20, 30], “b”: [40, 50, 60]})
print(df.apply(func, axis=1))
0 0.25
1 0.40
2 0.50
dtype: float64
3.8.3 向量化函數
def f(x, y):
if y == 0:
return np.nan
return x / y
df = pd.DataFrame({“a”: [10, 20, 30], “b”: [40, 0, 60]})
print(f(df[“a”], df[“b”])) # ValueError
上述代碼會報錯,因為y==0中,y為向量而0為標量。
(1)可以通過np.vectorize()將函數向量化來進行計算
def f(x, y):
if y == 0:
return np.nan
return x / y
df = pd.DataFrame({“a”: [10, 20, 30], “b”: [40, 0, 60]})
f_vec = np.vectorize(f)
print(f_vec(df[“a”], df[“b”])) # [0.25 nan 0.5 ]
(2)也可以使用@np.vectorize裝飾器將函數向量化
@np.vectorize
def f(x, y):
if y == 0:
return np.nan
return x / y
df = pd.DataFrame({“a”: [10, 20, 30], “b”: [40, 0, 60]})
print(f(df[“a”], df[“b”])) # [0.25 nan 0.5 ]
3.9 Padas的數據聚合、轉換、過濾函數
3.9.1 DataFrameGroupBy對象
對DataFrame對象調用groupby()方法后,會返回DataFrameGroupBy對象。
df = pd.read_csv(“data/employees.csv”) # 讀取員工數據
print(df.groupby(“department_id”)) # 按department_id分組,返回DataFrameGroupBy對象
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x0000024FCBAFD700>
這個對象可以看成是一種特殊形式的 DataFrame,里面隱藏著若干組數據,但是在沒有應用累計函數之前不會計算。GroupBy對象是一種非常靈活的抽象類型。在大多數場景中,可以將它看成是DataFrame的集合。
1)查看分組
通過groups屬性查看分組結果,返回一個字典,字典的鍵是分組的標簽,值是屬于該組的所有索引的列表。
print(df.groupby(“department_id”).groups) # 查看分組結果
{10.0: [100], 20.0: [101, 102], 30.0: [14, 15, 16, 17, 18, 19]…
通過get_group()方法獲取分組。
print(df.groupby(“department_id”).get_group(50)) # 獲取分組為50的數據
employee_id first_name last_name email…
20 120 Matthew Weiss MWEISS…
21 121 Adam Fripp AFRIPP…
22 122 Payam Kaufling PKAUFLIN…
2)按列取值
print(df.groupby(“department_id”)[“salary”]) # 按department_id分組,取salary列
<pandas.core.groupby.generic.SeriesGroupBy object at 0x0000022456D6F2F0>
這里從原來的DataFrame中取某個列名作為一個Series組。與GroupBy對象一樣,直到我們運行累計函數,才會開始計算。
print(df.groupby(“department_id”)[“salary”].mean()) # 計算每個部門平均薪資
department_id
10.0 4400.000000
20.0 9500.000000
30.0 4150.000000
3)按組迭代
GroupBy對象支持直接按組進行迭代,返回的每一組都是Series或DataFrame。
for dept_id,group in df.groupby(“department_id”):
print(f"當前組為{dept_id},組里的數據情況{group.shape}:“)
print(group.iloc[:,0:3])
print(”-------------------")
當前組為10.0,組里的數據情況(1, 10):
employee_id first_name last_name
100 200 Jennifer Whalen
-------------------
當前組為20.0,組里的數據情況(2, 10):
employee_id first_name last_name
101 201 Michael Hartstein
102 202 Pat Fay
…
4)按多字段分組
salary_mean = df.groupby([“department_id”, “job_id”])[
[“salary”, “commission_pct”]
].mean() # 按department_id和job_id分組
print(salary_mean.index) # 查看分組后的索引
MultiIndex([( 10.0, ‘AD_ASST’),
( 20.0, ‘MK_MAN’),
( 20.0, ‘MK_REP’),
( 30.0, ‘PU_CLERK’),
( 30.0, ‘PU_MAN’),
…
print(salary_mean.columns) # 查看分組后的列
Index([‘salary’, ‘commission_pct’], dtype=‘object’)
按多個字段分組后得到的索引為復合索引。
可通過reset_index()方法重置索引。
print(salary_mean.reset_index())
department_id job_id salary commission_pct
0 10.0 AD_ASST 4400.000000 NaN
1 20.0 MK_MAN 13000.000000 NaN
2 20.0 MK_REP 6000.000000 NaN
3 30.0 PU_CLERK 2780.000000 NaN
4 30.0 PU_MAN 11000.000000 NaN
也可以在分組的時候通過as_index = False參數(默認是True)重置索引。
salary_mean = df.groupby([“department_id”, “job_id”], as_index=False)[
[“salary”, “commission_pct”]
].mean() # 按department_id和job_id分組
print(salary_mean)
department_id job_id salary commission_pct
0 10.0 AD_ASST 4400.000000 NaN
1 20.0 MK_MAN 13000.000000 NaN
2 20.0 MK_REP 6000.000000 NaN
3 30.0 PU_CLERK 2780.000000 NaN
4 30.0 PU_MAN 11000.000000 NaN
5)cut()
pandas.cut()用于將連續數據(如數值型數據)分割成離散的區間。可以使用cut()來將數據劃分為不同的類別或范圍,通常用于數據的分箱處理。
cut()部分參數說明:
參數 說明
x 要分箱的數組或Series,通常是數值型數據。
bins 切分區間的數值列表或者整數。如果是整數,則表示將數據均勻地分成多少個區間。如果是列表,則需要指定每個區間的邊界。
right 默認True,表示每個區間的右端點是閉區間,即包含右端點。如果設置為False,則左端點為閉區間。
labels 傳入一個列表指定每個區間的標簽。
df = pd.read_csv(“data/employees.csv”) # 加載員工數據
salary = pd.cut(df.iloc[9:16][“salary”], 3)
print(salary)
9 (8366.667, 11000.0]
10 (5733.333, 8366.667]
11 (5733.333, 8366.667]
12 (5733.333, 8366.667]
13 (5733.333, 8366.667]
14 (8366.667, 11000.0]
15 (3092.1, 5733.333]
Name: salary, dtype: category
Categories (3, interval[float64, right]): [(3092.1, 5733.333] < (5733.333, 8366.667] <
(8366.667, 11000.0]]
salary = pd.cut(df.iloc[9:16][“salary”], [0, 10000, 20000])
print(salary)
9 (0, 10000]
10 (0, 10000]
11 (0, 10000]
12 (0, 10000]
13 (0, 10000]
14 (10000, 20000]
15 (0, 10000]
Name: salary, dtype: category
Categories (2, interval[int64, right]): [(0, 10000] < (10000, 20000]]
salary = pd.cut(df.iloc[9:16][“salary”], 3, labels=[“low”, “medium”, “high”])
print(salary)
9 high
10 medium
11 medium
12 medium
13 medium
14 high
15 low
Name: salary, dtype: category
Categories (3, object): [‘low’ < ‘medium’ < ‘high’]
3.9.2 分組聚合
df.groupby(“分組字段”)[“要聚合的字段”].聚合函數()
df.groupby([“分組字段”, “分組字段2”, …])[[“要聚合的字段”, “要聚合的字段2”, …]].聚合函數()
1)常用聚合函數
方法 說明
sum() 求和
mean() 平均值
min() 最小值
max() 最大值
var() 方差
std() 標準差
median() 中位數
quantile() 指定位置的分位數,如quantile(0.5)
describe() 常見統計信息
size() 所有元素的個數
count() 非空元素的個數
first 第一行
last 最后一行
nth 第n行
2)一次計算多個統計值
可以通過agg()或aggregate()進行更復雜的操作,如一次計算多個統計值。
df = pd.read_csv(“data/employees.csv”) # 讀取員工數據
按department_id分組,計算salary的最小值,中位數,最大值
print(df.groupby(“department_id”)[“salary”].agg([“min”, “median”, “max”]))
min median max
department_id
10.0 4400.0 4400.0 4400.0
20.0 6000.0 9500.0 13000.0
30.0 2500.0 2850.0 11000.0
40.0 6500.0 6500.0 6500.0
50.0 2100.0 3100.0 8200.0
3)多個列計算不同的統計值
也可以在agg()中傳入字典,對多個列計算不同的統計值。
df = pd.read_csv(“data/employees.csv”) # 讀取員工數據
按department_id分組,統計job_id的種類數,commission_pct的平均值
print(df.groupby(“department_id”).agg({“job_id”: “nunique”, “commission_pct”: “mean”}))
job_id commission_pct
department_id
10.0 1 NaN
20.0 2 NaN
30.0 2 NaN
40.0 1 NaN
50.0 3 NaN
4)重命名統計值
可以在agg()后通過rename()對統計后的列重命名。
df = pd.read_csv(“data/employees.csv”) # 讀取員工數據
按department_id分組,統計job_id的種類數,commission_pct的平均值
print(
df.groupby(“department_id”)
.agg(
{“job_id”: “nunique”, “commission_pct”: “mean”},
)
.rename(
columns={“job_id”: “工種數”, “commission_pct”: “傭金比例平均值”},
)
)
工種數 傭金比例平均值
department_id
10.0 1 NaN
20.0 2 NaN
30.0 2 NaN
40.0 1 NaN
50.0 3 NaN
5)自定義函數
可以向agg()中傳入自定義函數進行計算。
df = pd.read_csv(“data/employees.csv”) # 讀取員工數據
def f(x):
“”“統計每個部門員工last_name的首字母”“”
result = set()
for i in x:
result.add(i[0])
return result
print(df.groupby(“department_id”)[“last_name”].agg(f))
department_id
10.0 {W}
20.0 {F, H}
30.0 {B, T, R, C, K, H}
40.0 {M}
50.0 {O, E, K, S, W, L, P, D, C, V, B, T, M, J, F, …
3.9.3 分組轉換
聚合操作返回的是對組內全量數據縮減過的結果,而轉換操作會返回一個新的全量數據。數據經過轉換之后,其形狀與原來的輸入數據是一樣的。
1)通過transform()將每一組的樣本數據減去各組的均值,實現數據標準化
df = pd.read_csv(“data/employees.csv”) # 讀取員工數據
print(df.groupby(“department_id”)[“salary”].transform(lambda x: x - x.mean()))
0 4666.666667
1 -2333.333333
2 -2333.333333
3 3240.000000
4 240.000000
2)通過transform()按分組使用平均值填充缺失值
df = pd.read_csv(“data/employees.csv”) # 讀取員工數據
na_index = pd.Series(df.index.tolist()).sample(30) # 隨機挑選30條數據
df.loc[na_index, “salary”] = pd.NA # 將這30條數據的salary設置為缺失值
print(df.groupby(“department_id”)[“salary”].agg([“size”, “count”])) # 查看每組數據總數與非空數據數
def fill_missing(x):
# 使用平均值填充,如果平均值也為NaN,用0填充
if np.isnan(x.mean()):
return 0
return x.fillna(x.mean())
df[“salary”] = df.groupby(“department_id”)[“salary”].transform(fill_missing)
print(df.groupby(“department_id”)[“salary”].agg([“size”, “count”])) # 查看每組數據總數與非空數據數
3.9.4 分組過濾
過濾操作可以讓我們按照分組的屬性丟棄若干數據。
例如,我們可能只需要保留commission_pct不包含空值的分組的數據。
commission_pct_filter = df.groupby(“department_id”).filter(
lambda x: x[“commission_pct”].notnull().all()
) # 按department_id分組,過濾掉commission_pct包含空值的分組
print(commission_pct_filter)
3.10 Pandas透視表
3.10.1 什么是透視表
透視表(pivot table)是各種電子表格程序和其他數據分析軟件中一種常見的數據匯總工具。它可以根據多個行分組鍵和多個列分組鍵對數據進行聚合,并根據行和列上的分組鍵將數據分配到各個矩形區域中。
3.10.2 pivot_table()
pandas中提供了DataFrame.pivot_table()和pandas.pivot_table()方法來生成透視表。兩者的區別是pandas.pivot_table()需要額外傳入一個data參數指定對哪個DataFrame進行處理。
pivot_table()的參數如下:
參數 說明
values 待聚合的列,默認聚合所有數值列。
index 用作透視表行索引的列。即通過哪個(些)行來對數據進行分組,行索引決定了透視表的行維度。
columns 用作透視表列索引的列。即通過哪個(些)列來對數據進行分組,列索引決定了透視表的列維度。
aggfunc 聚合函數或函數列表,默認為mean。
fill_value 用于替換結果表中的缺失值。
margins 是否在透視表的邊緣添加匯總行和列,顯示總計。默認值是 False,如果設置為 True,會添加“總計”行和列,方便查看數據的總體匯總。
dropna 是否排除包含缺失值的行和列。默認為 True,即如果某個組合的行列數據中包含缺失值,則會被排除在外。如果設置為 False,則會保留這些含有缺失值的行和列。
observerd 是否顯示所有組合數據,True:只顯示實際存在的組合
3.10.3 案例:睡眠質量分析透視表
使用sleep(睡眠健康和生活方式)數據集,其中包含13個字段:
? person_id:每個人的唯一標識符。
? gender:個人的性別(男/女)。
? age:個人的年齡(以歲為單位)。
? occupation:個人的職業或就業狀況(例如辦公室職員、體力勞動者、學生)。
? sleep_duration:每天的睡眠總小時數。
? sleep_quality:睡眠質量的主觀評分,范圍從 1(差)到 10(極好)。
? physical_activity_level:每天花費在體力活動上的時間(以分鐘為單位)。
? stress_level:壓力水平的主觀評級,范圍從 1(低)到 10(高)。
? bmi_category:個人的 BMI 分類(體重過輕、正常、超重、肥胖)。
? blood_pressure:血壓測量,顯示為收縮壓與舒張壓的數值。
? heart_rate:靜息心率,以每分鐘心跳次數為單位。
? daily_steps:個人每天行走的步數。
? sleep_disorder:存在睡眠障礙(無、失眠、睡眠呼吸暫停)。
1)統計不同睡眠時間,不同壓力等級下的睡眠質量
df = pd.read_csv(“data/sleep.csv”)
sleep_duration_stage = pd.cut(df[“sleep_duration”], [0, 5, 6, 7, 8, 9, 10, 11, 12]) # 對睡眠時間進行劃分
stress_level_stage = pd.cut(df[“stress_level”], 4) # 對壓力等級進行劃分
print(df.pivot_table(values=“sleep_quality”, index=[sleep_duration_stage, stress_level_stage], aggfunc=“mean”))
sleep_quality
sleep_duration stress_level
(0, 5] (0.991, 3.25] 6.781818
(3.25, 5.5] 6.161538
(5.5, 7.75] 5.677778
(7.75, 10.0] 6.082353
(5, 6] (0.991, 3.25] 5.876923
(3.25, 5.5] 6.777778
(5.5, 7.75] 6.058333
(7.75, 10.0] 6.438462
2)添加職業作為列維度
print(
df.pivot_table(
values=“sleep_quality”, index=[sleep_duration_stage, stress_level_stage], columns=[“occupation”], aggfunc=“mean”
)
)
occupation Manual Labor Office Worker Retired Student
sleep_duration stress_level
(0, 5] (0.991, 3.25] 6.900000 6.350000 6.720000 6.750000
(3.25, 5.5] 3.300000 7.966667 6.060000 5.650000
(5.5, 7.75] 4.833333 6.900000 3.200000 6.533333
(7.75, 10.0] 7.200000 5.977778 5.225000 7.150000
(5, 6] (0.991, 3.25] 5.220000 6.433333 5.700000 6.533333
(3.25, 5.5] 5.000000 7.050000 6.900000 9.000000
(5.5, 7.75] 6.050000 5.300000 5.300000 7.200000
(7.75, 10.0] 6.475000 4.050000 NaN 7.100000
3)添加性別作為第二個列維度
print(
df.pivot_table(
values=“sleep_quality”,
index=[sleep_duration_stage, stress_level_stage],
columns=[“occupation”, “gender”],
aggfunc=“mean”,
)
)
occupation Manual Labor Office Worker Retired Student
gender Female Male Female Male Female Male Female Male
sleep_duration stress_level
(0, 5] (0.991, 3.25] 6.75 7.300000 6.700000 6.000 NaN 6.720000 6.100000 7.400000
(3.25, 5.5] 3.30 NaN 7.100000 9.700 4.850000 6.866667 5.300000 6.700000
(5.5, 7.75] 4.55 5.400000 5.900000 7.900 NaN 3.200000 6.850000 5.900000
(7.75, 10.0] 8.40 6.000000 5.180000 6.975 6.600000 4.766667 7.150000 NaN
(5, 6] (0.991, 3.25] 5.50 4.800000 8.200000 5.550 5.700000 NaN 8.150000 3.300000
(3.25, 5.5] 5.00 NaN 6.600000 7.500 6.700000 7.100000 9.000000 NaN
(5.5, 7.75] 6.60 5.500000 4.900000 6.100 4.450000 7.000000 7.066667 7.600000
(7.75, 10.0] 6.15 6.800000 NaN 4.050 NaN NaN 7.266667 6.975000
3.11 Pandas時間序列
3.11.1 Python中的日期與時間工具
Python基本的日期與時間功能都在標準庫的datetime模塊中。
from datetime import datetime
date1 = datetime(year=2000, month=1, day=1)
date2 = datetime.now()
print(date1) # 2000-01-01 00:00:00
print(date2) # 2025-01-01 00:00:00
print(date1.year) # 2000
print(date1.month) # 1
print(date1.day) # 1
print(date2.weekday()) # 5
print(date2.strftime(“%A”)) # Saturday
print(date2 - date1) # 18263 days, 0:00:00
3.11.2 pandas中的日期與時間
pandas的日期時間類型默認是datetime64[ns]。
? 針對時間戳數據,pandas提供了Timestamp類型。它本質上是Python原生datetime類型的替代品,但是在性能更好的numpy.datetime64類型的基礎上創建。對應的索引數據結構是DatetimeIndex。
? 針對時間周期數據,pandas提供了Period類型。這是利用numpy.datetime64類型將固定頻率的時間間隔進行編碼。對應的索引數據結構是PeriodIndex。
? 針對時間增量或持續時間,pandas提供了Timedelta類型。Timedelta是一種代替Python原生datetime.timedelta類型的高性能數據結構,同樣是基于numpy.timedelta64類型。對應的索引數據結構是TimedeltaIndex。
1)datetime64
to_datetime()可以解析許多日期與時間格式。對to_datetime()傳遞一個日期會返回一個Timestamp類型,傳遞一個時間序列會返回一個DatetimeIndex類型。
注意:Timestamp 是 pandas 對 datetime64 數據類型的一個封裝。datetime64 是 NumPy 中的一種數據類型,用于表示日期和時間,而 pandas 基于 datetime64 構建了 Timestamp 類,以便更方便地在 pandas 的數據結構(如 DataFrame 和 Series)中處理日期時間數據。當 pd.to_datetime 接收單個日期時間值時,會返回 Timestamp 對象。不過,要是使用 dtype 屬性查看,得到的卻是 datetime64[ns],這是因為 dtype 反映的是底層存儲的數據類型,而 Timestamp 對象底層存儲的數據類型就是 datetime64[ns]。
print(pd.to_datetime(“2015-01-01”))
2015-01-01 00:00:00
print(pd.to_datetime([“4th of July, 2015”, “2015-Jul-6”, “07-07-2015”, “20150708”], format=“mixed”))
DatetimeIndex([‘2015-07-04’, ‘2015-07-06’, ‘2015-07-07’, ‘2015-07-08’], dtype=‘datetime64[ns]’, freq=None)
在加載數據時,可以通過to_datetime()將數據中的列解析為datetime64。
df = pd.read_csv(“data/weather.csv”)
print(df[“date”].tail())
1456 2015-12-27
1457 2015-12-28
1458 2015-12-29
1459 2015-12-30
1460 2015-12-31
Name: date, dtype: object
print(pd.to_datetime(df[“date”]).tail())
1456 2015-12-27
1457 2015-12-28
1458 2015-12-29
1459 2015-12-30
1460 2015-12-31
Name: date, dtype: datetime64[ns]
在加載數據時也可以通過parse_dates參數將指定列解析為datetime64。
df = pd.read_csv(“data/weather.csv”, parse_dates=[0])
print(df[“date”].tail())
1456 2015-12-27
1457 2015-12-28
1458 2015-12-29
1459 2015-12-30
1460 2015-12-31
Name: date, dtype: datetime64[ns]
2)提取日期的各個部分
(1)提取Timestamp
d = pd.Timestamp(“2015-01-01 09:08:07.123456”)
print(d.year) # 2015
print(d.month) # 1
print(d.day) # 1
print(d.hour) # 9
print(d.minute) # 8
print(d.second) # 7
print(d.microsecond) # 123456
(2)對于Series對象,需要使用dt訪問器
df = pd.read_csv(“data/weather.csv”, parse_dates=[0])
df_date = pd.to_datetime(df[“date”])
df[“year”] = df_date.dt.year
df[“month”] = df_date.dt.month
df[“day”] = df_date.dt.day
print(df[[“date”, “year”, “month”, “day”]].tail())
date year month day
1456 2015-12-27 2015 12 27
1457 2015-12-28 2015 12 28
1458 2015-12-29 2015 12 29
1459 2015-12-30 2015 12 30
1460 2015-12-31 2015 12 31
3)period
可以通過to_period()方法和一個頻率代碼將datetime64類型轉換成period類型。
df = pd.read_csv(“data/weather.csv”)
df[“quarter”] = pd.to_datetime(df[“date”]).dt.to_period(“Q”) # 將 年-月-日 轉換為 年季度
print(df[[“date”, “quarter”]].head())
date quarter
0 2012-01-01 2012Q1
1 2012-01-02 2012Q1
2 2012-01-03 2012Q1
3 2012-01-04 2012Q1
4 2012-01-05 2012Q1
4)timedelta64
當用一個日期減去另一個日期,返回的結果是timedelta64類型。
df = pd.read_csv(“data/weather.csv”, parse_dates=[0])
df_date = pd.to_datetime(df[“date”])
timedelta = df_date - df_date[0]
print(timedelta.head())
0 0 days
1 1 days
2 2 days
3 3 days
4 4 days
Name: date, dtype: timedelta64[ns]
3.11.3 使用時間作為索引
1)DatetimeIndex
將datetime64類型的數據設置為索引,得到的就是DatetimeIndex。
df = pd.read_csv(“data/weather.csv”)
df[“date”] = pd.to_datetime(df[“date”]) # 將date列轉換為datetime64類型
df.set_index(“date”, inplace=True) # 將date列設置為索引
df.info()
<class ‘pandas.core.frame.DataFrame’>
DatetimeIndex: 1461 entries, 2012-01-01 to 2015-12-31
將時間作為索引后可以直接使用時間進行切片取值。
print(df.loc[“2013-01”:“2013-06”]) # 獲取2013年1~6月的數據
precipitation temp_max temp_min wind weather
date
2013-01-01 0.0 5.0 -2.8 2.7 sun
2013-01-02 0.0 6.1 -1.1 3.2 sun
… … … … … …
2013-06-29 0.0 30.0 18.3 1.7 sun
2013-06-30 0.0 33.9 17.2 2.5 sun
print(df.loc[“2015”]) # 獲取2015年所有數據
precipitation temp_max temp_min wind weather
date
2015-01-01 0.0 5.6 -3.2 1.2 sun
2015-01-02 1.5 5.6 0.0 2.3 rain
… … … … … …
2015-12-30 0.0 5.6 -1.0 3.4 sun
2015-12-31 0.0 5.6 -2.1 3.5 sun
也可以通過between_time()和at_time()獲取某些時刻的數據。
df.between_time(“9:00”, “11:00”) # 獲取9:00到11:00之間的數據
df.at_time(“3:33”) # 獲取3:33的數據
2)TimedeltaIndex
將timedelta64類型的數據設置為索引,得到的就是TimedeltaIndex。
df = pd.read_csv(“data/weather.csv”, parse_dates=[0])
df_date = pd.to_datetime(df[“date”])
df[“timedelta”] = df_date - df_date[0] # 得到timedelta64類型的數據
df.set_index(“timedelta”, inplace=True) # 將timedelta列設置為索引
df.info()
<class ‘pandas.core.frame.DataFrame’>
TimedeltaIndex: 1461 entries, 0 days to 1460 days
將時間作為索引后可以直接使用時間進行切片取值。
print(df.loc[“0 days”:“5 days”])
date precipitation temp_max temp_min wind weather
timedelta
0 days 2012-01-01 0.0 12.8 5.0 4.7 drizzle
1 days 2012-01-02 10.9 10.6 2.8 4.5 rain
2 days 2012-01-03 0.8 11.7 7.2 2.3 rain
3 days 2012-01-04 20.3 12.2 5.6 4.7 rain
4 days 2012-01-05 1.3 8.9 2.8 6.1 rain
5 days 2012-01-06 2.5 4.4 2.2 2.2 rain
3.11.4 生成時間序列
為了能更簡便地創建有規律的時間序列,pandas提供了date_range()方法。
1)date_range()
date_range()通過開始日期、結束日期和頻率代碼(可選)創建一個有規律的日期序列,默認的頻率是天。
print(pd.date_range(“2015-07-03”, “2015-07-10”))
DatetimeIndex([‘2015-07-03’, ‘2015-07-04’, ‘2015-07-05’, ‘2015-07-06’,
‘2015-07-07’, ‘2015-07-08’, ‘2015-07-09’, ‘2015-07-10’],
dtype=‘datetime64[ns]’, freq=‘D’)
此外,日期范圍不一定非是開始時間與結束時間,也可以是開始時間與周期數periods。
print(pd.date_range(“2015-07-03”, periods=5))
DatetimeIndex([‘2015-07-03’, ‘2015-07-04’, ‘2015-07-05’, ‘2015-07-06’,
‘2015-07-07’],
dtype=‘datetime64[ns]’, freq=‘D’)
可以通過freq參數設置時間頻率,默認值是D。此處改為h,按小時變化的時間戳。
print(pd.date_range(“2015-07-03”, periods=5, freq=“h”))
DatetimeIndex([‘2015-07-03 00:00:00’, ‘2015-07-03 01:00:00’,
‘2015-07-03 02:00:00’, ‘2015-07-03 03:00:00’,
‘2015-07-03 04:00:00’],
dtype=‘datetime64[ns]’, freq=‘h’)
2)時間頻率與偏移量
(1)可通過freq參數設置時間頻率
下表為常見時間頻率代碼與說明:
代碼 說明
D 天(calendar day,按日歷算,含雙休日)
B 天(business day,僅含工作日)
W 周(weekly)
ME / M 月末(month end)
BME 月末(business month end,僅含工作日)
MS 月初(month start)
BMS 月初(business month start,僅含工作日)
QE / Q 季末(quarter end)
BQE 季末(business quarter end,僅含工作日)
QS 季初(quarter start)
BQS 季初(business quarter start,僅含工作日)
YE / Y 年末(year end)
BYE 年末(business year end,僅含工作日)
YS 年初(year start)
BYS 年初(business year start,僅含工作日)
h 小時(hours)
bh 小時(business hours,工作時間)
min 分鐘(minutes)
s 秒(seconds)
ms 毫秒(milliseonds)
us 微秒(microseconds)
ns 納秒(nanoseconds)
(2)偏移量
可以在頻率代碼后面加三位月份縮寫字母來改變季、年頻率的開始時間。
? QE-JAN、BQE-FEB、QS-MAR、BQS-APR等
? YE-JAN、BYE-FEB、YS-MAR、BYS-APR等
print(pd.date_range(“2015-07-03”, periods=10, freq=“QE-JAN”)) # 設置1月為季度末
DatetimeIndex([‘2015-07-31’, ‘2015-10-31’, ‘2016-01-31’, ‘2016-04-30’,
‘2016-07-31’, ‘2016-10-31’, ‘2017-01-31’, ‘2017-04-30’,
‘2017-07-31’, ‘2017-10-31’],
dtype=‘datetime64[ns]’, freq=‘QE-JAN’)
同理,也可以在后面加三位星期縮寫字母來改變一周的開始時間。
? W-SUN、W-MON、W-TUE、W-WED等
print(pd.date_range(“2015-07-03”, periods=10, freq=“W-WED”)) # 設置周三為一周的第一天
DatetimeIndex([‘2015-07-08’, ‘2015-07-15’, ‘2015-07-22’, ‘2015-07-29’,
‘2015-08-05’, ‘2015-08-12’, ‘2015-08-19’, ‘2015-08-26’,
‘2015-09-02’, ‘2015-09-09’],
dtype=‘datetime64[ns]’, freq=‘W-WED’)
在這些代碼的基礎上,還可以將頻率組合起來創建的新的周期。例如,可以用小時(h)和分鐘(min)的組合來實現2小時30分鐘。
print(pd.date_range(“2015-07-03”, periods=10, freq=“2h30min”))
DatetimeIndex([‘2015-07-03 00:00:00’, ‘2015-07-03 02:30:00’,
‘2015-07-03 05:00:00’, ‘2015-07-03 07:30:00’,
‘2015-07-03 10:00:00’, ‘2015-07-03 12:30:00’,
‘2015-07-03 15:00:00’, ‘2015-07-03 17:30:00’,
‘2015-07-03 20:00:00’, ‘2015-07-03 22:30:00’],
dtype=‘datetime64[ns]’, freq=‘150min’)
3.11.5 重新采樣
處理時間序列數據時,經常需要按照新的頻率(更高頻率、更低頻率)對數據進行重新采樣。可以通過resample()方法解決這個問題。resample()方法以數據累計為基礎,會將數據按指定的時間周期進行分組,之后可以對其使用聚合函數。
df = pd.read_csv(“data/weather.csv”)
df[“date”] = pd.to_datetime(df[“date”])
df.set_index(“date”, inplace=True)
print(df[[“temp_max”, “temp_min”]].resample(“YE”).mean()) # 將數據按年分組,并計算每年的平均最高最低溫度
temp_max temp_min
date
2012-12-31 15.276776 7.289617
2013-12-31 16.058904 8.153973
2014-12-31 16.995890 8.662466
2015-12-31 17.427945 8.835616
3.12 Matplotlib可視化
3.12.1 Matplotlib簡介
1)什么是Matplotlib
Matplotlib是一個Python繪圖庫,廣泛用于創建各種類型的靜態、動態和交互式圖表。它是數據科學、機器學習、工程和科學計算領域中常用的繪圖工具之一。
? 支持多種圖表類型:折線圖(Line plots)、散點圖(Scatter plots)、柱狀圖(Bar charts)、直方圖(Histograms)、餅圖(Pie charts)、熱圖(Heatmaps)、箱型圖(Box plots)、極坐標圖(Polar plots)、3D圖(3D plots,配合 mpl_toolkits.mplot3d)。
? 高度自定義:允許用戶自定義圖表的每個部分,包括標題、軸標簽、刻度、圖例等。 支持多種顏色、字體和線條樣式。提供精確的圖形渲染控制,如坐標軸范圍、圖形大小、字體大小等。
? 兼容性:與NumPy、Pandas等庫緊密集成,特別適用于繪制基于數據框和數組的數據可視化。可以輸出到多種格式(如PNG、PDF、SVG、EPS等)。
? 交互式繪圖:在Jupyter Notebook 中,Matplotlib支持交互式繪圖,可以動態更新圖表。支持圖形縮放、平移等交互操作。
? 動態圖表:可以生成動畫(使用FuncAnimation類),為用戶提供動態數據的可視化。
2)不同開發環境下顯示圖形
? 在一個腳本文件中使用Matplotlib,那么顯示圖形的時候必須使用plt.show()。
? 在Notebook中使用Matplotlib,運行命令之后在每一個Notebook的單元中就會直接將PNG格式圖形文件嵌入在單元中。
3.12.2 兩種畫圖接口
Matplotlib有兩種畫圖接口:一個是便捷的MATLAB風格的有狀態的接口,另一個是功能更強大的面向對象接口。
1)狀態接口
import numpy as np
import matplotlib.pyplot as plt # 導入matplotlib
x = np.linspace(0, 10, 100) # 創建x軸的數據
y1 = np.sin(x) # 創建y軸的數據
y2 = np.cos(x) # 創建y軸的數據
plt.figure(figsize=(10, 6)) # 創建畫布,并指定畫布大小 10*6英寸
plt.subplot(2, 1, 1) # 創建2行1列個子圖,并指定第1個子圖
plt.xlim(0, 10) # 設置x軸的范圍
plt.ylim(-1, 1) # 設置y軸的范圍
plt.xlabel(“x”) # 設置x軸的標簽
plt.ylabel(“sin(x)”) # 設置y軸的標簽
plt.title(“sin”) # 設置子圖的標題
plt.plot(x, y1) # 繪制曲線
plt.subplot(2, 1, 2) # 創建2行1列個子圖,并指定第2個子圖
plt.xlim(0, 10) # 設置x軸的范圍
plt.ylim(-1, 1) # 設置y軸的范圍
plt.xlabel(“x”) # 設置x軸的標簽
plt.ylabel(“cos(x)”) # 設置y軸的標簽
plt.title(“cos”) # 設置子圖的標題
plt.plot(x, y2)
plt.show() # 顯示圖像
2)面向對象接口
import numpy as np
import matplotlib.pyplot as plt # 導入matplotlib
x = np.linspace(0, 10, 100) # 創建x軸的數據
y1 = np.sin(x) # 創建y軸的數據
y2 = np.cos(x) # 創建y軸的數據
fig, ax = plt.subplots(2, figsize=(10, 6)) # 創建畫布,并指定畫布大小
ax[0].set_xlim(0, 10) # 設置x軸的范圍
ax[0].set_ylim(-1, 1) # 設置y軸的范圍
ax[0].set_xlabel(“x”) # 設置x軸的標簽
ax[0].set_ylabel(“sin(x)”) # 設置y軸的標簽
ax[0].set_title(“sin”) # 設置子圖的標題
ax[0].plot(x, y1) # 繪制曲線
ax[1].plot(x, y2) # 繪制曲線
ax[1].set_xlim(0, 10) # 設置x軸的范圍
ax[1].set_ylim(-1, 1) # 設置y軸的范圍
ax[1].set_xlabel(“x”) # 設置x軸的標簽
ax[1].set_ylabel(“cos(x)”) # 設置y軸的標簽
ax[1].set_title(“cos”) # 設置子圖的標題
plt.show()
3.12.3 單變量可視化
使用weather(天氣)數據集。其中包含6個字段:
? date:日期,年-月-日格式。
? precipitation:降水量。
? temp_max:最高溫度。
? temp_min:最低溫度。
? wind:風力。
? weather:天氣狀況。
加載數據:
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import rcParams
rcParams[“font.sans-serif”] = [“SimHei”] # 指定中文字體
rcParams[“axes.unicode_minus”] = False # 解決負號顯示問題
df = pd.read_csv(“data/weather.csv”)
df.info() # 查看數據集信息
RangeIndex: 1461 entries, 0 to 1460
Data columns (total 6 columns):
# Column Non-Null Count Dtype
— ------ -------------- -----
0 date 1461 non-null object
1 precipitation 1461 non-null float64
2 temp_max 1461 non-null float64
3 temp_min 1461 non-null float64
4 wind 1461 non-null float64
5 weather 1461 non-null object
dtypes: float64(4), object(2)
memory usage: 68.6+ KB
使用直方圖將降水量分組并繪制每組出現頻次。
fig = plt.figure()
ax1 = fig.add_subplot(1, 1, 1)
ax1.hist(df[“precipitation”], bins=5) # 繪制直方圖,將降水量均勻分為5組
ax1.set_xlabel(“降水量”)
ax1.set_ylabel(“出現頻次”)
plt.show()
3.12.4 多變量可視化
1)雙變量
使用散點圖呈現降水量隨最高氣溫變化的大致趨勢。
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import rcParams
rcParams[“font.sans-serif”] = [“SimHei”] # 指定中文字體
rcParams[“axes.unicode_minus”] = False # 解決負號顯示問題
df = pd.read_csv(“data/weather.csv”)
fig = plt.figure()
ax1 = fig.add_subplot(1, 1, 1)
ax1.scatter(df[“temp_max”], df[“precipitation”]) # 繪制散點圖,橫軸為最高氣溫,縱軸為降水量
ax1.set_xlabel(“最高氣溫”)
ax1.set_ylabel(“降水量”)
plt.show()
2)多變量
使用散點圖呈現降水量隨最高氣溫變化的大致趨勢,用不同顏色區分不同年份的數據。
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import rcParams
rcParams[“font.sans-serif”] = [“SimHei”] # 指定中文字體
rcParams[“axes.unicode_minus”] = False # 解決負號顯示問題
def year_color(x):
“”“添加一列,為不同年份的數據添加不同的顏色”“”
match x.year:
case 2012:
return “r”
case 2013:
return “g”
case 2014:
return “b”
case 2015:
return “k”
df = pd.read_csv(“data/weather.csv”)
df[“date”] = pd.to_datetime(df[“date”])
df[“color”] = df[“date”].apply(year_color)
fig = plt.figure()
ax1 = fig.add_subplot(1, 1, 1)
繪制散點圖,橫軸為最高氣溫,縱軸為降水量
c設置顏色,alpha設置透明度
ax1.scatter(df[“temp_max”], df[“precipitation”], c=df[“color”], alpha=0.5)
ax1.set_xlabel(“最高氣溫”)
ax1.set_ylabel(“降水量”)
plt.show()
3.13 Pandas可視化
pandas提供了非常方便的繪圖功能,可以直接在DataFrame或Series上調用plot()方法來生成各種類型的圖表。底層實現依賴于Matplotlib,pandas的繪圖功能集成了許多常見的圖形類型,易于使用。
3.13.1 單變量可視化
使用sleep(睡眠健康和生活方式)數據集,其中包含13個字段:
? person_id:每個人的唯一標識符。
? gender:個人的性別(男/女)。
? age:個人的年齡(以歲為單位)。
? occupation:個人的職業或就業狀況(例如辦公室職員、體力勞動者、學生)。
? sleep_duration:每天的睡眠總小時數。
? sleep_quality:睡眠質量的主觀評分,范圍從 1(差)到 10(極好)。
? physical_activity_level:每天花費在體力活動上的時間(以分鐘為單位)。
? stress_level:壓力水平的主觀評級,范圍從 1(低)到 10(高)。
? bmi_category:個人的 BMI 分類(體重過輕、正常、超重、肥胖)。
? blood_pressure:血壓測量,顯示為收縮壓與舒張壓的數值。
? heart_rate:靜息心率,以每分鐘心跳次數為單位。
? daily_steps:個人每天行走的步數。
? sleep_disorder:存在睡眠障礙(無、失眠、睡眠呼吸暫停)。
加載數據:
import pandas as pd
df = pd.read_csv(“data/sleep.csv”)
df.info() # 查看數據集信息
RangeIndex: 400 entries, 0 to 399
Data columns (total 13 columns):
# Column Non-Null Count Dtype
— ------ -------------- -----
0 person_id 400 non-null int64
1 gender 400 non-null object
2 age 400 non-null int64
3 occupation 400 non-null object
4 sleep_duration 400 non-null float64
5 sleep_quality 400 non-null float64
6 physical_activity_level 400 non-null int64
7 stress_level 400 non-null int64
8 bmi_category 400 non-null object
9 blood_pressure 400 non-null object
10 heart_rate 400 non-null int64
11 daily_steps 400 non-null int64
12 sleep_disorder 110 non-null object
dtypes: float64(2), int64(6), object(5)
memory usage: 40.8+ KB
1)柱狀圖
柱狀圖用于展示類別數據的分布情況。它通過一系列矩形的高度(或長度)來展示數據值,適合對比不同類別之間的數量或頻率。簡單直觀,容易理解和比較各類別數據。
使用柱狀圖展示不同睡眠時長的數量。
pd.cut(df[“sleep_duration”], [0, 5, 6, 7, 8, 9, 10, 11, 12]).value_counts().plot.bar(
color=[“red”, “green”, “blue”, “yellow”, “cyan”, “magenta”, “black”, “purple”]
)
2)折線圖
折線圖通常用于展示連續數據的變化趨勢。它通過一系列數據點連接成的線段來表示數據的變化。能夠清晰地展示數據的趨勢和波動。
使用折線圖展示不同睡眠時長的數量。
pd.cut(df[“sleep_duration”], [0, 5, 6, 7, 8, 9, 10, 11, 12]).value_counts().sort_index().plot()
3)面積圖
面積圖是折線圖的一種變體,線下的區域被填充顏色,用于強調數據的總量或變化。可以更直觀地展示數據量的變化,適合用來展示多個分類的累計趨勢。
使用面積圖展示不同睡眠時長的數量。
pd.cut(df[“sleep_duration”], [0, 5, 6, 7, 8, 9, 10, 11, 12]).value_counts().sort_index().plot.area()
4)直方圖
直方圖用于展示數據的分布情況。它將數據范圍分成多個區間,并通過矩形的高度顯示每個區間內數據的頻率或數量。可以揭示數據分布的模式,如偏態、峰度等。
使用直方圖展示不同睡眠時長的數量。
df[“sleep_duration”].value_counts().plot.hist()
5)餅狀圖
餅狀圖用于展示一個整體中各個部分所占的比例。它通過一個圓形圖形分割成不同的扇形,每個扇形的角度與各部分的比例成正比。能夠快速展示各部分之間的比例關系,但不適合用于展示過多的類別或比較數值差異較小的部分。
使用餅狀圖展示不同睡眠時長的占比。
pd.cut(df[“sleep_duration”], [0, 5, 6, 7, 8, 9, 10, 11, 12]).value_counts().sort_index().plot.pie()
3.13.2 雙變量可視化
1)散點圖
散點圖通過在二維坐標系中繪制數據點來展示兩組數值數據之間的關系。能夠揭示兩個變量之間的相關性和趨勢。
繪制睡眠時間與睡眠質量的散點圖。
df.plot.scatter(x=“sleep_duration”, y=“sleep_quality”)
2)蜂窩圖
蜂窩圖是散點圖的擴展,通常用于表示大量數據點之間的關系。它通過將數據點分布在一個六邊形網格中,每個六邊形的顏色代表其中的數據密度。適合展示大量數據點,避免了散點圖中的過度重疊問題。
繪制睡眠時間與睡眠質量的蜂窩圖。
df.plot.hexbin(x=“sleep_duration”, y=“sleep_quality”, gridsize=10)
3)堆疊圖
堆疊圖用于展示多個數據系列的累積變化。常見的堆疊圖包括堆疊柱狀圖、堆疊面積圖等。它通過將每個數據系列堆疊在前一個系列之上,展示數據的累積情況。能夠清晰地展示不同部分的相對貢獻,適合多個數據系列的比較。
繪制睡眠時間與睡眠質量的堆疊圖。
df[“sleep_quality_stage”] = pd.cut(df[“sleep_quality”], range(11))
df[“sleep_duration_stage”] = pd.cut(df[“sleep_duration”], [0, 5, 6, 7, 8, 9, 10, 11, 12])
df_pivot_table = df.pivot_table(
values=“person_id”, index=“sleep_quality_stage”, columns=“sleep_duration_stage”, aggfunc=“count”
)
df_pivot_table.plot.bar()
設置stacked=True,會將柱體堆疊。
df_pivot_table.plot.bar(stacked=True)
4)折線圖
df_pivot_table.plot.line()
3.14 Seaborn可視化
3.14.1 什么是Seaborn
Seaborn是一個基于Matplotlib的Python可視化庫,旨在簡化數據可視化的過程。它提供了更高級的接口,用于生成漂亮和復雜的統計圖表,同時也能保持與Pandas數據結構的良好兼容性。
3.14.2 單變量可視化
使用penguins(企鵝🐧)數據集,其中包含7個字段:
? species:企鵝種類(Adelie、Gentoo、Chinstrap)。
? island:觀測島嶼(Torgersen, Biscoe, Dream)。
? bill_length_mm:喙(嘴)長度(毫米)。
? bill_depth_mm:喙深度(毫米)。
? flipper_length_mm:腳蹼長度(毫米)。
? body_mass_g:體重(克)。
? sex:性別(Male、Female)。
加載數據:
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
plt.rcParams[“font.sans-serif”] = [“KaiTi”]
penguins = pd.read_csv(“data/penguins.csv”)
penguins.dropna(inplace=True)
penguins.info()
<class ‘pandas.core.frame.DataFrame’>
Index: 333 entries, 0 to 343
Data columns (total 7 columns):
# Column Non-Null Count Dtype
— ------ -------------- -----
0 species 333 non-null object
1 island 333 non-null object
2 bill_length_mm 333 non-null float64
3 bill_depth_mm 333 non-null float64
4 flipper_length_mm 333 non-null float64
5 body_mass_g 333 non-null float64
6 sex 333 non-null object
dtypes: float64(4), object(3)
memory usage: 20.8+ KB
1)直方圖
繪制不同種類企鵝數量的直方圖。
sns.histplot(data=penguins, x=“species”)
2)核密度估計圖
核密度估計圖(KDE,Kernel Density Estimate Plot)是一種用于顯示數據分布的統計圖表,它通過平滑直方圖的方法來估計數據的概率密度函數,使得分布圖看起來更加連續和平滑。核密度估計是一種非參數方法,用于估計隨機變量的概率密度函數。其基本思想是,將每個數據點視為一個“核”(通常是高斯分布),然后將這些核的貢獻相加以形成平滑的密度曲線。
繪制喙長度的核密度估計圖。
sns.kdeplot(data=penguins, x=“bill_length_mm”)
在histplot()中設置kde=True也可以得到核密度估計圖。
sns.histplot(data=penguins, x=“bill_length_mm”, kde=True)
3)計數圖
計數圖用于繪制分類變量的計數分布圖,顯示每個類別在數據集中出現的次數,是分析分類數據非常直觀的工具,可以快速了解類別的分布情況。
繪制不同島嶼企鵝數量的計數圖。
sns.countplot(data=penguins, x=“island”)
3.14.3 雙變量可視化
1)散點圖
繪制橫軸為體重,縱軸為腳蹼長度的散點圖。可通過hue參數設置不同組別進行對比。
sns.scatterplot(data=penguins, x=“body_mass_g”, y=“flipper_length_mm”, hue=“sex”)
也可以通過regplot()函數繪制散點圖,同時會擬合回歸曲線。可以通過fit_reg=False關閉擬合。
sns.regplot(data=penguins, x=“body_mass_g”, y=“flipper_length_mm”)
也可以通過lmplot()函數繪制基于hue參數的分組回歸圖。
sns.lmplot(data=penguins, x=“body_mass_g”, y=“flipper_length_mm”, hue=“sex”)
也可以通過jointplot()函數繪制在每個軸上包含單個變量的散點圖。
sns.jointplot(data=penguins, x=“body_mass_g”, y=“flipper_length_mm”)
2)蜂窩圖
通過jointplot()函數,設置kind="hex"來繪制蜂窩圖。
sns.jointplot(data=penguins, x=“body_mass_g”, y=“flipper_length_mm”, kind=“hex”)
3)二維核密度估計圖
通過kdeplot()函數,同時設置x參數和y參數來繪制二維核密度估計圖。
sns.kdeplot(data=penguins, x=“body_mass_g”, y=“flipper_length_mm”)
通過fill=True設置為填充,通過cbar=True設置顯示顏色示意條。
sns.kdeplot(data=penguins, x=“body_mass_g”, y=“flipper_length_mm”, fill=True, cbar=True)
4)條形圖
條形圖會按x分組對y進行聚合,通過estimator參數設置聚合函數,并通過errorbar設置誤差條,誤差條默認會顯示。可以通過誤差條顯示抽樣數據統計結果的可能統計范圍,如果數據不是抽樣數據, 可以設置為None來關閉誤差條。
sns.barplot(data=penguins, x=“species”, y=“bill_length_mm”, estimator=“mean”, errorbar=None)
5)箱線圖
箱線圖是一種用于展示數據分布、集中趨勢、散布情況以及異常值的統計圖表。它通過五個關鍵的統計量(最小值、第一四分位數、中位數、第三四分位數、最大值)來展示數據的分布情況。
箱線圖通過箱體和須來表現數據的分布,能夠有效地顯示數據的偏斜、分散性以及異常值。箱線圖的組成部分:
? 箱體(Box):
? 下四分位數(Q1):數據集下 25% 的位置,箱體的下邊緣。
? 上四分位數(Q3):數據集下 75% 的位置,箱體的上邊緣。
? 四分位間距(IQR, Interquartile Range):Q3 和 Q1 之間的距離,用來衡量數據的離散程度。
? 中位數(Median):箱體內部的水平線,表示數據集的中位數。
? 須(Whiskers):
? 下須:從 Q1 向下延伸,通常是數據集中最小值與 Q1 的距離,直到沒有超過1.5倍 IQR 的數據點為止。
? 上須:從 Q3 向上延伸,通常是數據集中最大值與 Q3 的距離,直到沒有超過1.5倍 IQR 的數據點為止。
? 異常值(Outliers):
? 超過1.5倍 IQR 的數據被認為是異常值,通常用點標記出來。異常值是數據中相對于其他數據點而言“非常大”或“非常小”的值。
sns.boxplot(data=penguins, x=“species”, y=“bill_length_mm”)
6)小提琴圖
小提琴圖(Violin Plot) 是一種結合了箱線圖和核密度估計圖(KDE)的可視化圖表,用于展示數據的分布情況、集中趨勢、散布情況以及異常值。小提琴圖不僅可以顯示數據的基本統計量(如中位數和四分位數),還可以展示數據的概率密度,提供比箱線圖更豐富的信息。
sns.violinplot(data=penguins, x=“species”, y=“bill_length_mm”)
7)成對關系圖
成對關系圖是一種用于顯示多個變量之間關系的可視化工具。它可以展示各個變量之間的成對關系,并且通過不同的圖表形式幫助我們理解數據中各個變量之間的相互作用。
對角線上的圖通常顯示每個變量的分布(如直方圖或核密度估計圖),幫助觀察每個變量的單變量特性。其他位置展示所有變量的兩兩關系,用散點圖表示。
sns.pairplot(data=penguins, hue=“species”)
通常情況下成對關系圖左上和右下對應位置的圖的信息是相同的,可以通過PairGrid()為每個區域設置不同的圖類型。
pair_grid = sns.PairGrid(data=penguins, hue=“species”)
通過 map 方法在網格上繪制不同的圖形
pair_grid.map_upper(sns.scatterplot) # 上三角部分使用散點圖
pair_grid.map_lower(sns.kdeplot) # 下三角部分使用核密度估計圖
pair_grid.map_diag(sns.histplot) # 對角線部分使用直方圖
3.14.4 多變量可視化
多數繪圖函數都支持使用hue參數設置一個類別變量,統計時按此類別分組統計并在繪圖時使用顏色區分。
例如對小提琴圖設置hue參數添加性別類別:
sns.violinplot(data=penguins, x=“species”, y=“bill_length_mm”, hue=“sex”, split=True)
3.14.5 Seaborn樣式
在Seaborn中,樣式(style)控制了圖表的整體外觀,包括背景色、網格線、刻度線等元素。Seaborn提供了一些內置的樣式選項,可以通過seaborn.set_style()來設置當前圖表的樣式。常見的樣式有以下幾種:
? white:純白背景,沒有網格線。
? dark:深色背景,帶有網格線。
? whitegrid:白色背景,帶有網格線。
? darkgrid:深色背景,帶有網格線(默認樣式)。
sns.set_style(“darkgrid”)
sns.histplot(data=penguins, x=“island”, kde=True)
第 4 章 綜合案例:房地產市場洞察與價值評估
4.1 業務背景
在房地產市場中,準確的房價預測和深入的市場分析對于房產開發商、投資者以及購房者都至關重要。房產開發商需要根據市場趨勢和不同因素對房價的影響來制定合理的定價策略,優化項目規劃;投資者需要評估房產的潛在價值和投資回報率,做出明智的投資決策;購房者則希望了解市場行情,找到性價比高的房產。
某大型房地產數據研究機構收集了大量不同地區的房屋銷售數據,這些數據包含了房屋的各種屬性信息以及銷售相關信息。為了更好地服務于市場參與者,該機構計劃對這些數據進行全面深入的分析,挖掘數據背后的規律和價值。具體目標包括:
? 探究不同房屋特征(如臥室數量、浴室數量、居住面積等)對房價的影響程度,以便為房價預測模型提供依據。
? 分析不同地區(以郵政編碼劃分)的房地產市場差異,了解各地區的房價水平、市場活躍度等情況。
? 研究房屋的建造年份、翻新年份等時間因素對房價的影響,以及不同時間段的市場趨勢變化。
? 通過可視化手段直觀展示數據的分布和關系,為決策提供清晰的參考。
4.2 數據源介紹
字段名 含義 數據類型 說明
id 房屋銷售記錄的唯一標識符 整數 用于唯一標識每一條房屋銷售記錄
date 房屋銷售日期 日期時間類型 記錄房屋實際完成銷售的日期,可用于時間序列分析,觀察不同時間段的市場趨勢
price 房屋銷售價格 數值型 反映房屋在銷售時的成交金額,是分析的核心指標之一,受多種房屋特征和市場因素影響
bedrooms 臥室數量 整數 體現房屋的居住功能布局,臥室數量的多少會影響房屋的整體實用性和市場需求
bathrooms 浴室數量 整數 同樣是影響房屋舒適度和實用性的重要因素,與臥室數量共同影響房屋的居住體驗
sqft_living 居住面積(平方英尺) 數值型 指房屋內部可供居住使用的實際面積,是影響房價的關鍵因素之一
sqft_lot 土地面積(平方米) 數值型 包括房屋所在土地的總面積,土地面積大小會影響房屋的整體價值和使用空間
floors 樓層數 整數 房屋的樓層數量會影響房屋的視野、采光、私密性等方面,進而對房價產生影響
waterfront 是否臨水 整數(0 或 1) 0 表示房屋不臨水,1 表示房屋臨水,臨水房屋通常具有更高的景觀價值和市場價格
view 景觀評分 整數(0 - 4) 對房屋周邊景觀的評分,評分越高表示景觀越好,景觀質量會影響房屋的吸引力和價格
condition 房屋狀況評分 整數(1 - 5) 反映房屋的整體狀況,包括房屋的結構、裝修、設施等方面的維護情況
grade 房屋整體質量評分 整數(1 - 13) 綜合評估房屋的建筑質量、設計水平等因素,是衡量房屋價值的重要指標
sqft_above 地上面積(平方米) 數值型 指房屋地面以上部分的建筑面積,不包括地下室面積
sqft_basement 地下室面積(平方米) 數值型 地下室面積可作為額外的存儲空間或功能區域,對房屋的實用性和價值有一定影響
yr_built 建造年份 整數 記錄房屋的建成時間,房屋的建造年份會影響房屋的折舊程度、建筑風格和市場競爭力
yr_renovated 翻新年份 整數 0 表示房屋未進行過翻新,非 0 值表示房屋進行翻新的具體年份,翻新可以提升房屋的價值和居住體驗
zipcode 郵政編碼 整數 用于標識房屋所在的地理位置區域,不同的郵政編碼區域可能具有不同的市場特征和房價水平
lat 緯度 數值型 房屋所在位置的緯度坐標,結合經度可確定房屋的具體地理位置
long 經度 數值型 房屋所在位置的經度坐標,與緯度共同用于地理空間分析
4.3 待統計指標及說明
4.3.1 數值型列的描述性統計指標
? 均值(Mean):一組數據的平均值,反映數據的集中趨勢。例如,房價的均值可以讓我們了解該地區房屋的平均銷售價格水平。
? 中位數(Median):將數據按升序或降序排列后,位于中間位置的數值。當數據存在極端值時,中位數比均值更能代表數據的一般水平。
? 標準差(Standard Deviation):衡量數據相對于均值的離散程度。標準差越大,說明數據越分散;反之,則越集中。比如房價的標準差可以反映該地區房價的波動情況。
? 最小值(Minimum):數據集中的最小數值,可用于了解數據的下限。
? 最大值(Maximum):數據集中的最大數值,可用于了解數據的上限。
? 四分位數(Quartiles):包括第一四分位數(Q1,25% 分位數)、第二四分位數(Q2,即中位數,50% 分位數)和第三四分位數(Q3,75% 分位數),能幫助了解數據的分布情況。
4.3.2 不同特征與房價的相關性
? 使用皮爾遜相關系數衡量特征與房價之間的線性關系強度和方向,系數絕對值越接近 1,相關性越強;正系數表示正相關,負系數表示負相關。
4.3.3 按郵政編碼、是否翻新、房齡分組的統計指標
? 平均房價:各郵政編碼區域內房屋的平均銷售價格,用于對比不同區域的房價水平。
? 平均居住面積:各區域內房屋居住面積的平均值,反映區域房屋規模情況。
? 平均臥室數量:各區域內房屋臥室數量的平均值,體現區域房屋居住功能布局。
4.3.4 時間序列分析指標
? 每年平均房價(Average Price per Year):按銷售年份分組計算的房屋平均銷售價格,可用于觀察房價隨時間的變化趨勢。
4.4 代碼實現步驟
4.4.1 數據讀取
1)代碼
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rcParams
rcParams[“font.sans-serif”] = [“SimHei”] # 指定中文字體
讀取 CSV 文件
data = pd.read_csv(‘E:\大模型課程\05_尚硅谷大模型技術之Numpy&Pandas\2.資料\data\house_sales.csv’)
print(‘數據基本信息:’)
data.info()
2)代碼說明
使用 pandas 的 read_csv 函數讀取 house_sales.csv 文件,將數據存儲在 DataFrame 對象 data 中,方便后續處理,并查看數據基本信息。
4.4.2 數據清洗
1)代碼
檢查缺失值
missing_values = data.isnull().sum()
print(‘各列缺失值數量:’)
print(missing_values)
處理缺失值,這里簡單地刪除包含缺失值的行
data = data.dropna()
檢查異常值,以房價為例,使用 IQR 方法
Q1 = data[‘price’].quantile(0.25)
Q3 = data[‘price’].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
data = data[(data[‘price’] >= lower_bound) & (data[‘price’] <= upper_bound)]
2)代碼說明
缺失值處理:使用 isnull().sum() 統計各列缺失值數量,然后用 dropna() 刪除包含缺失值的行。
異常值處理:使用 IQR(Inter - Quartile Range,四分位距)方法來檢測和處理房價數據中的異常值。以房價為例,通過計算第一四分位數 Q1、第三四分位數 Q3 和四分位距 IQR,確定上下限,篩選出合理范圍內的數據。
? data[‘price’].quantile(0.25):quantile 是 pandas 中用于計算分位數的方法。這里 0.25 表示計算 25% 分位數,也就是第一四分位數 Q1。第一四分位數意味著有 25% 的數據小于這個值。
? data[‘price’].quantile(0.75):同理,0.75 表示計算 75% 分位數,即第三四分位數 Q3。有 75% 的數據小于這個值。
? 四分位距 IQR 是第三四分位數 Q3 與第一四分位數 Q1 的差值。它衡量了數據中間 50% 的數據的分散程度。
? lower_bound:通過 Q1 - 1.5 * IQR 計算出異常值的下限。如果某個數據點小于這個下限,就可能被視為異常值。
? upper_bound:通過 Q3 + 1.5 * IQR 計算出異常值的上限。如果某個數據點大于這個上限,也可能被視為異常值。
? 這里的 1.5 是一個常用的系數,在很多情況下可以有效地識別出大部分異常值,但在某些特殊場景下可能需要調整。
? 使用布爾索引來篩選數據。(data[‘price’] >= lower_bound) & (data[‘price’] <= upper_bound) 表示篩選出 price 列中值大于等于下限且小于等于上限的數據,將這些數據重新賦值給 data,從而去除了可能的異常值。
4.4.3 數據類型轉換
1)代碼
將日期列轉換為日期類型
data[‘date’] = pd.to_datetime(data[‘date’])
2)代碼說明
使用 pandas 的 to_datetime 函數將 date 列轉換為日期類型,便于進行時間序列分析。
4.4.4 創建新的特征
1)代碼
計算房屋的使用年限
data[‘age’] = data[‘date’].dt.year - data[‘yr_built’]
創建新特征:是否翻新
data[‘is_renovated’] = data[‘yr_renovated’].apply(lambda x: 1 if x > 0 else 0)
2)代碼說明
計算房屋使用年限:通過銷售日期的年份減去建造年份,得到房屋的使用年限,存儲在新列 age 中,這個特征可能會對房價產生影響。
創建是否翻新特征:使用 apply 方法和 lambda 函數對 yr_renovated 列進行判斷,若值大于 0 則表示房屋已翻新,將 is_renovated 列對應的值設為 1,否則設為 0,以便后續分析翻新因素對房價的影響。
4.4.5 數據探索性分析-描述性統計
1)代碼
選擇數值型列
numeric_columns = data.select_dtypes(include=[np.number]).columns
計算描述性統計信息
description = data[numeric_columns].describe(percentiles=[0.25, 0.5, 0.75])
print(‘數值型列的描述性統計:’)
print(description)
2)代碼說明
data.select_dtypes(include=[np.number]) 選擇數據集中的數值型列,并獲取其列名存儲在 numeric_columns 中。
data[numeric_columns].describe(percentiles=[0.25, 0.5, 0.75]) 計算數值型列的描述性統計信息,包括均值、中位數、標準差、最小值、最大值、四分位數等,并將結果存儲在 description 中,幫助我們了解各數值特征的分布情況。
4.4.6 數據探索性分析-相關性統計
1)代碼
計算不同特征與房價的相關性
correlation = data[numeric_columns].corr()
print(‘各特征與房價的相關性:’)
print(correlation[‘price’])
2)代碼說明
對數值型列使用 corr 方法計算相關系數矩陣,提取 price 列得到各特征與房價的相關性。
4.4.7 按照郵政編碼分組分析
1)代碼
按郵政編碼分組,計算每組的平均房價、平均居住面積、平均臥室數量
zipcode_stats = data.groupby(‘zipcode’).agg({
‘price’: ‘mean’,
‘sqft_living’: ‘mean’,
‘bedrooms’: ‘mean’
})
zipcode_stats.columns = [‘avg_price’, ‘avg_sqft_living’, ‘avg_bedrooms’]
print(‘不同郵政編碼區域的統計信息:’)
print(zipcode_stats)
2)代碼說明
使用 data.groupby(‘zipcode’) 按郵政編碼對數據進行分組。
agg 方法對分組后的數據進行聚合操作,分別計算每組的平均房價、平均居住面積和平均臥室數量。
對結果的列名進行重命名,使其更具可讀性,并打印輸出,可對比不同郵政編碼區域的房屋特征情況。
4.4.8 按照是否翻新分組分析
1)代碼
按是否翻新分組,計算每組的平均房價、平均居住面積、平均臥室數量
renovation_stats = data.groupby(‘is_renovated’).agg({
‘price’: ‘mean’,
‘sqft_living’: ‘mean’,
‘bedrooms’: ‘mean’
})
renovation_stats.columns = [‘avg_price’, ‘avg_sqft_living’, ‘avg_bedrooms’]
print(‘是否翻新分組的統計信息:’)
print(renovation_stats)
2)代碼說明
按 is_renovated 特征對數據進行分組,分析翻新和未翻新房屋在房價、居住面積和臥室數量等方面的差異。同樣使用 agg 方法進行聚合計算,得到相應的統計信息并打印。
4.4.9 按照房齡分組分析
1)代碼
按房屋使用年限分組(簡單分為 5 個區間)
data[‘age_group’] = pd.cut(data[‘age’], bins=5)
age_stats = data.groupby(‘age_group’).agg({
‘price’: ‘mean’,
‘sqft_living’: ‘mean’,
‘bedrooms’: ‘mean’
})
print(‘按房屋使用年限分組的統計信息:’)
print(age_stats)
2)代碼說明
使用 pd.cut 函數將房屋使用年限 age 劃分為 5 個區間,創建新列 age_group。
按 age_group 分組,計算每組的平均房價、平均居住面積和平均臥室數量,了解不同使用年限房屋的特征差異。
4.4.10 時間序列分析-每年平均房價
1)代碼
按年份分組,計算每年的平均房價
yearly_avg_price = data.groupby(data[‘date’].dt.year)[‘price’].mean()
print(‘每年的平均房價:’)
print(yearly_avg_price)
2)代碼說明
使用 data.groupby(data[‘date’].dt.year) 按銷售日期的年份對數據進行分組。
對每組的 price 列計算均值,得到每年的平均房價,并存儲在 yearly_avg_price 中進行打印輸出,可觀察房價隨時間的變化趨勢。
4.4.11 時間序列分析-不同翻新情況平均房價
1)代碼
按年份和是否翻新分組,計算每年不同翻新情況的平均房價
yearly_renovation_avg_price = data.groupby([data[‘date’].dt.year, ‘is_renovated’])[‘price’].mean()
print(‘每年不同翻新情況的平均房價:’)
print(yearly_renovation_avg_price)
2)代碼說明
按銷售年份和是否翻新進行分組,計算每年翻新和未翻新房屋的平均房價,能讓我們看到在不同年份,翻新因素對房價的影響變化。
4.4.12 可視化
1)房價分布直方圖
房價分布直方圖
plt.figure(figsize=(10, 6))
plt.hist(data[‘price’], bins=30, edgecolor=‘k’)
plt.title(‘房價分布直方圖’)
plt.xlabel(‘房價’)
plt.ylabel(‘頻數’)
plt.show()
使用 plt.hist 函數繪制房價的分布直方圖,bins=30 控制柱子的數量,edgecolor=‘k’ 為柱子添加黑色邊框。添加標題和坐標軸標簽,使圖形更易理解,最后使用 plt.show() 顯示圖形。
2)臥室數量與房價的散點圖
臥室數量與房價的散點圖
plt.figure(figsize=(10, 6))
plt.scatter(data[‘bedrooms’], data[‘price’])
plt.title(‘臥室數量與房價的關系’)
plt.xlabel(‘臥室數量’)
plt.ylabel(‘房價’)
plt.show()
使用 plt.scatter 函數繪制臥室數量與房價的散點圖,直觀展示兩者之間的關系。
3)各特征與房價的相關性熱力圖
各特征與房價的相關性熱力圖
plt.figure(figsize=(12, 8))
plt.imshow(correlation, cmap=‘coolwarm’, interpolation=‘nearest’)
plt.colorbar()
plt.xticks(range(len(correlation.columns)), correlation.columns, rotation=90)
plt.yticks(range(len(correlation.columns)), correlation.columns)
plt.title(‘各特征與房價的相關性熱力圖’)
plt.show()
使用 plt.imshow 函數繪制相關性熱力圖,cmap=‘coolwarm’ 設置顏色映射,interpolation=‘nearest’ 控制插值方式。添加顏色條和坐標軸標簽,顯示各特征與房價的相關性,最后顯示圖形。
4)不同郵政編碼區域平均房價的柱狀圖
不同郵政編碼區域平均房價的柱狀圖
plt.figure(figsize=(12, 6))
plt.bar(zipcode_stats.index.astype(str), zipcode_stats[‘avg_price’])
plt.title(‘不同郵政編碼區域的平均房價’)
plt.xlabel(‘郵政編碼’)
plt.ylabel(‘平均房價’)
plt.xticks(rotation=45)
plt.show()
使用 plt.bar 函數繪制不同郵政編碼區域平均房價的柱狀圖,將 zipcode 轉換為字符串類型。設置圖形標題和坐標軸標簽,旋轉 x 軸標簽避免重疊后顯示圖形。
5)每年平均房價的折線圖
每年平均房價的折線圖
plt.figure(figsize=(10, 6))
plt.plot(yearly_avg_price.index, yearly_avg_price)
plt.title(‘每年平均房價趨勢’)
plt.xlabel(‘年份’)
plt.ylabel(‘平均房價’)
plt.show()
使用 plt.plot 函數繪制每年平均房價的折線圖,展示房價隨時間的變化趨勢。
6)不同翻新情況的房價箱線圖
不同翻新情況的房價箱線圖
plt.figure(figsize=(10, 6))
data.boxplot(column=‘price’, by=‘is_renovated’)
plt.title(‘不同翻新情況的房價箱線圖’)
plt.xlabel(‘是否翻新’)
plt.xticks([1, 2], [‘未翻新’, ‘已翻新’])
plt.ylabel(‘房價’)
plt.suptitle(‘’) # 去掉默認的標題
plt.show()
使用 data.boxplot 方法繪制不同翻新情況的房價箱線圖,展示翻新和未翻新房屋房價的分布情況
7)房屋使用年限與房價的散點圖
房屋使用年限與房價的散點圖
plt.figure(figsize=(10, 6))
plt.scatter(data[‘age’], data[‘price’])
plt.title(‘房屋使用年限與房價的關系’)
plt.xlabel(‘房屋使用年限’)
plt.ylabel(‘房價’)
plt.show()
使用 plt.scatter 函數繪制房屋使用年限與房價的散點圖,觀察兩者之間的關系。
4.4.13 完整代碼
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rcParams
rcParams[“font.sans-serif”] = [“SimHei”] # 指定中文字體
讀取 CSV 文件
data = pd.read_csv(‘E:\大模型課程\05_尚硅谷大模型技術之Numpy&Pandas\2.資料\data\house_sales.csv’)
print(‘數據基本信息:’)
data.info()
檢查缺失值
missing_values = data.isnull().sum()
print(‘各列缺失值數量:’)
print(missing_values)
處理缺失值,這里簡單地刪除包含缺失值的行
data = data.dropna()
檢查異常值,以房價為例,使用 IQR 方法
Q1 = data[‘price’].quantile(0.25)
Q3 = data[‘price’].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
data = data[(data[‘price’] >= lower_bound) & (data[‘price’] <= upper_bound)]
將日期列轉換為日期類型
data[‘date’] = pd.to_datetime(data[‘date’])
計算房屋的使用年限
data[‘age’] = data[‘date’].dt.year - data[‘yr_built’]
創建新特征:是否翻新
data[‘is_renovated’] = data[‘yr_renovated’].apply(lambda x: 1 if x > 0 else 0)
選擇數值型列
numeric_columns = data.select_dtypes(include=[np.number]).columns
計算描述性統計信息
description = data[numeric_columns].describe(percentiles=[0.25, 0.5, 0.75])
print(‘數值型列的描述性統計:’)
print(description)
計算不同特征與房價的相關性
correlation = data[numeric_columns].corr()
print(‘各特征與房價的相關性:’)
print(correlation[‘price’])
按郵政編碼分組,計算每組的平均房價、平均居住面積、平均臥室數量
zipcode_stats = data.groupby(‘zipcode’).agg({
‘price’: ‘mean’,
‘sqft_living’: ‘mean’,
‘bedrooms’: ‘mean’
})
zipcode_stats.columns = [‘avg_price’, ‘avg_sqft_living’, ‘avg_bedrooms’]
print(‘不同郵政編碼區域的統計信息:’)
print(zipcode_stats)
按是否翻新分組,計算每組的平均房價、平均居住面積、平均臥室數量
renovation_stats = data.groupby(‘is_renovated’).agg({
‘price’: ‘mean’,
‘sqft_living’: ‘mean’,
‘bedrooms’: ‘mean’
})
renovation_stats.columns = [‘avg_price’, ‘avg_sqft_living’, ‘avg_bedrooms’]
print(‘是否翻新分組的統計信息:’)
print(renovation_stats)
按房屋使用年限分組(簡單分為 5 個區間)
data[‘age_group’] = pd.cut(data[‘age’], bins=5)
age_stats = data.groupby(‘age_group’).agg({
‘price’: ‘mean’,
‘sqft_living’: ‘mean’,
‘bedrooms’: ‘mean’
})
print(‘按房屋使用年限分組的統計信息:’)
print(age_stats)
按年份分組,計算每年的平均房價
yearly_avg_price = data.groupby(data[‘date’].dt.year)[‘price’].mean()
print(‘每年的平均房價:’)
print(yearly_avg_price)
按年份和是否翻新分組,計算每年不同翻新情況的平均房價
yearly_renovation_avg_price = data.groupby([data[‘date’].dt.year, ‘is_renovated’])[‘price’].mean()
print(‘每年不同翻新情況的平均房價:’)
print(yearly_renovation_avg_price)
房價分布直方圖
plt.figure(figsize=(10, 6))
plt.hist(data[‘price’], bins=30, edgecolor=‘k’)
plt.title(‘房價分布直方圖’)
plt.xlabel(‘房價’)
plt.ylabel(‘頻數’)
plt.show()
臥室數量與房價的散點圖
plt.figure(figsize=(10, 6))
plt.scatter(data[‘bedrooms’], data[‘price’])
plt.title(‘臥室數量與房價的關系’)
plt.xlabel(‘臥室數量’)
plt.ylabel(‘房價’)
plt.show()
各特征與房價的相關性熱力圖
plt.figure(figsize=(12, 8))
plt.imshow(correlation, cmap=‘coolwarm’, interpolation=‘nearest’)
plt.colorbar()
plt.xticks(range(len(correlation.columns)), correlation.columns, rotation=90)
plt.yticks(range(len(correlation.columns)), correlation.columns)
plt.title(‘各特征與房價的相關性熱力圖’)
plt.show()
不同郵政編碼區域平均房價的柱狀圖
plt.figure(figsize=(12, 6))
plt.bar(zipcode_stats.index.astype(str), zipcode_stats[‘avg_price’])
plt.title(‘不同郵政編碼區域的平均房價’)
plt.xlabel(‘郵政編碼’)
plt.ylabel(‘平均房價’)
plt.xticks(rotation=45)
plt.show()
每年平均房價的折線圖
plt.figure(figsize=(10, 6))
plt.plot(yearly_avg_price.index, yearly_avg_price)
plt.title(‘每年平均房價趨勢’)
plt.xlabel(‘年份’)
plt.ylabel(‘平均房價’)
plt.show()
不同翻新情況的房價箱線圖
plt.figure(figsize=(10, 6))
data.boxplot(column=‘price’, by=‘is_renovated’)
plt.title(‘不同翻新情況的房價箱線圖’)
plt.xlabel(‘是否翻新’)
plt.xticks([1, 2], [‘未翻新’, ‘已翻新’])
plt.ylabel(‘房價’)
plt.suptitle(‘’) # 去掉默認的標題
plt.show()
房屋使用年限與房價的散點圖
plt.figure(figsize=(10, 6))
plt.scatter(data[‘age’], data[‘price’])
plt.title(‘房屋使用年限與房價的關系’)
plt.xlabel(‘房屋使用年限’)
plt.ylabel(‘房價’)
plt.show()