?3.3.1 ?創建與訪問
什么是?DataFrame?
DataFrame 是?Pandas 中的核心數據結構之一,多行多列表格數據,類似于?Excel 表格?或?SQL 查詢結果。
它是一個?二維表格結構,具有行索引(index)和列標簽(columns)。
Python
df = pd.DataFrame({"name": ["Alice", "Bob"],"score": [90, 80]
})
DataFrame中的數據是以一個或多個二維塊存放的(而不是列表、字典或別的一維數據結構)。它可以被看做由Series組成的字典(共同用一個索引)。提供了各種功能來進行數據訪問、篩選、分割、合并、重塑、聚合以及轉換等操作,廣泛用于數據分析、清洗、轉換、可視化等任務。
DataFrame的創建
Python
# 通過series來創建
import pandas as pd
import numpy as np
np.random.seed(42)
s1 = pd.Series(np.random.randint(0,10,6))
np.random.seed(41)
s2 = pd.Series(np.random.randint(0,20,6))
df = pd.DataFrame({"s1":s1,"s2":s2})
直接通過字典創建DataFrame
Python
import pandas as pd
df = pd.DataFrame({ ???"name": ["Alice", "Bob"], ???"score": [90, 80]})
print(df)
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
通過字典創建時指定列的順序和行索引
Bash
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
獲取DataFrame數據
方法分類 | 語法示例 | 描述 | 返回值類型 | 是否支持切片/條件索引 |
列選擇 | df['col'] | 選擇單列(返回Series) | Series | ? |
df[['col1', 'col2']] | 選擇多列(返回DataFrame) | DataFrame | ||
行選擇 | df.loc[row_label] | 通過行標簽選擇單行(返回Series) | Series | ?(標簽切片) |
df.loc[start:end] | 通過標簽切片選擇多行(閉區間) | DataFrame | ||
df.iloc[row_index] | 通過行位置選擇單行(從0開始) | Series | ?(位置切片) | |
df.iloc[start:end] | 通過位置切片選擇多行(左閉右開) | DataFrame | ||
行列組合選擇 | df.loc[row_labels, col_labels] | 通過標簽選擇行和列(如df.loc['a':'b', ['col1','col2']]) | Series/DataFrame | ? |
df.iloc[row_idx, col_idx] | 通過位置選擇行和列(如df.iloc[0:2, [1,3]]) | Series/DataFrame | ||
條件篩選 | df[df['col'] > 3] | 通過布爾條件篩選行 | DataFrame | ? |
df.query("col1 > 3 & col2 < 10") | 使用表達式篩選(需字符串表達式) | DataFrame | ||
快速訪問 | df.at[row_label, 'col'] | 快速訪問單個值(標簽索引,高效) | 標量值 | ? |
df.iat[row_idx, col_idx] | 快速訪問單個值(位置索引,高效) | 標量值 | ||
頭部/尾部 | df.head(n) | 返回前n行(默認5) | DataFrame | ? |
df.tail(n) | 返回后n行(默認5) | DataFrame | ||
樣本抽樣 | df.sample(n=3) | 隨機抽取n行 | DataFrame | |
索引重置 | df.reset_index() | 重置索引(原索引變為列) | DataFrame | |
設置索引 | df.set_index('col') | 指定某列作為新索引 | DataFrame |
- loc** vs ****iloc**??
- loc:基于**標簽**(index/column names),切片為**閉區間**(如df.loc['a':'c']包含'c')。??
- iloc:基于**整數位置**(從0開始),切片為**左閉右開**(如df.iloc[0:2]不包含索引2)。??
- 布爾條件篩選??
- 支持組合條件(需用&、|,并用括號分隔條件):??
Python
df[(df['col1'] > 3) & (df['col2'] == 'A')]
- at**/****iat**** vs ****loc****/****iloc**??
- at/iat:僅用于**訪問單個值**,速度更快。??
- loc/iloc:支持多行/列選擇,功能更靈活。??
獲取一列數據
Python# 訪問數據
print(df['name']) ?#訪問某列數據
print(df.score)# df["col"] / df.col
df["name"] ??????# 返回?Series
df.name ?????
df[["name"]] ????# 返回?DataFrame
獲取多列數據
Python
df[["date", "temp_max", "temp_min"]] ?# 獲取多列數據
print(df[['name','score']]) # 訪問多列數據
獲取行數據
loc:通過行標簽獲取數據
Python
df.loc[1] ?# 獲取行標簽為1的數據
df.loc[[1, 10, 100]] ?# 獲取行標簽分別為1、10、100的數據
iloc:通過行位置獲取數據
Python
df.iloc[0] ?# 獲取行位置為0的數據
df.iloc[-1] ?# 獲取行位置為最后一位的數據
獲取指定單元格
Python
df.loc[101, "name"] ???# 標簽訪問
df.iloc[0, 1] ?????????# 位置訪問
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"]] ?# 通過行列標簽獲取數據
查看部分數據
通過head()、tail()獲取前n行或后n行
Python
print(df.head())
print(df.tail(10))
使用布爾索引篩選數據
Bash
# 條件篩選
df['score']>70
print(df[df.score>70])
print(df[(df['score']>70) & (df['age']<20)])
# 隨機抽樣
df.sample(2)
常用屬性
屬性 | 說明 |
index | DataFrame的行索引 |
columns | DataFrame的列標簽 |
values | DataFrame的值 |
ndim | DataFrame的維度 |
shape | DataFrame的形狀 |
size | DataFrame的元素個數 |
dtypes | DataFrame的元素類型 |
T | 行列轉置 |
loc[] | 顯式索引,按行列標簽索引或切片 |
iloc[] | 隱式索引,按行列位置索引或切片 |
at[] | 使用行列標簽訪問單個元素 |
iat[] | 使用行列位置訪問單個元素 |
Python
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.2 常用方法與統計
方法 | 說明 |
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條數據 |
Bash
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"))# 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"))
在Pandas的?DataFrame?方法里,axis?是一個非常重要的參數,它用于指定操作的方向。
axis?參數可以取兩個主要的值,即?0?或?'index',以及?1?或?'columns'?,其含義如下:
- axis=0?或?axis='index':表示操作沿著行的方向進行,也就是對每一列的數據進行處理。例如,當計算每列的均值時,就是對每列中的所有行數據進行計算。
- axis=1?或?axis='columns':表示操作沿著列的方向進行,也就是對每行的數據進行處理。例如,當計算每行的總和時,就是對每行中的所有列數據進行計算。
3.3.3?運算
標量運算
標量與每個元素進行計算。
Python
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 ?趙六趙六?? 20df1?= 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.4 數據的導入與導出
導出數據
方法 | 說明 |
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() | 保存到數據庫。 |
Python
import?os
import?pandas?as?pdos.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")
導入數據
方法 | 說明 |
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() | 加載數據庫中的數據。 |
Python
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.5 數據清洗與預處理
章節 | 核心內容 | 關鍵知識點 |
1. 缺失值處理 | 檢測、刪除和填充缺失值的方法 | isna(), dropna(), fillna(), 前向/后向填充, 均值/中位數填充 |
2. 重復數據處理 | 識別和刪除重復行 | duplicated(), drop_duplicates(), 按列去重, 保留首次/最后一次出現 |
3. 數據類型轉換 | 強制類型轉換、日期/分類數據處理 | astype(), to_datetime(), 分類數據優化, 數值格式化 |
4. 數據重塑與變形 | 行列轉置、寬表長表轉換、分列操作 | T轉置, melt(), pivot(), str.split()分列 |
5. 文本數據處理 | 字符串清洗、正則提取、大小寫轉換 | str.lower(), str.replace(), str.extract(), 空格處理 |
6. 數據分箱與離散化 | 數值分箱(等寬/等頻) | pd.cut(), pd.qcut(), 離散化應用場景 |
7. 其他常用轉換 | 重命名列、索引操作、函數應用、內存優化 | rename(), set_index(), apply(), 類型優化減少內存占用 |
1. ?缺失值處理
方法/操作 | 語法示例 | 描述 |
檢測缺失值 | df.isna()?或?df.isnull() | 返回布爾矩陣,標記缺失值(NaN或None) |
統計缺失值 | df.isna().sum() | 每列缺失值數量統計 |
刪除缺失值 | df.dropna() | 刪除包含缺失值的行(默認) |
df.dropna(axis=1) | 刪除包含缺失值的列 | |
df.dropna(subset=['col1']) | 僅刪除指定列的缺失值行 | |
填充缺失值 | df.fillna(value) | 用固定值填充(如df.fillna(0) |
df.fillna(method='ffill') | 用前一個非缺失值填充(向前填充) | |
df.fillna(method='bfill') | 用后一個非缺失值填充(向后填充) | |
df.fillna(df.mean()) | 用列均值填充 |
pandas中的缺失值
- NaN (Not a Number) 是缺失值的標志
- 方法:?isna(), notna()
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更強調數據的可用性或存在性。
Plain Text
s?= pd.Series([np.nan, None, pd.NA])
print(s)
# 0 ? ? NaN
# 1 ? ?None
# 2 ? ?<NA>
# dtype: object
print(s.isnull())
# 0 ? ?True
# 1 ? ?True
# 2 ? ?True
# dtype: bool
加載數據中包含缺失值
Python
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參數設置是否將空白值設置為缺失值。
Python
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參數將指定值設置為缺失值。
Python
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
查看缺失值
通過isnull()查看缺失值數量
Python
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
剔除缺失值
通過dropna()方法來剔除缺失值。
Series剔除缺失值
Python
s?= pd.Series([1, pd.NA, None])
print(s)
# 0 ? ? ? 1
# 1 ? ?<NA>
# 2 ? ?None
# dtype: object
print(s.dropna())
# 0 ? ?1
# dtype: object
DataFrame剔除缺失值
無法從DataFrame中單獨剔除一個值,只能剔除缺失值所在的整行或整列。默認情況下,dropna()會剔除任何包含缺失值的整行數據。
Python
df?= pd.DataFrame([[1, pd.NA, 2], [2, 3, 5], [pd.NA, 4, 6]])
print(df)
# ? ? ? 0 ? ? 1 ?2
# 0 ? ? 1 ?<NA> ?2
# 1 ? ? 2 ? ? 3 ?5
# 2 ?<NA> ? ? 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 ?<NA> ?2# 1 ? ? 2 ? ? 3 ?5# 2 ?<NA> ? ? 4 ?6print(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 ?<NA> ? ? 2# 1 ?<NA> ?<NA> ? ? 5# 2 ?<NA> ?<NA> ?<NA>print(df.dropna(how="all")) ?# 如果所有值都是缺失值,則刪除這一行# ? ? ? 0 ? ? 1 ?2# 0 ? ? 1 ?<NA> ?2# 1 ?<NA> ?<NA> ?5print(df.dropna(thresh=2)) ?# 如果至少有2個值不是缺失值,則保留這一行# ? ?0 ? ? 1 ?2# 0 ?1 ?<NA> ?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 ?<NA> ? ? 2# 1 ?<NA> ?<NA> ? ? 5# 2 ?<NA> ?<NA> ?<NA>print(df.dropna(subset=[0])) ?# 如果0列有缺失值,則刪除這一行# ? ?0 ? ? 1 ?2# 0 ?1 ?<NA> ?2
填充缺失值
- 使用固定值填充
通過fillna()方法,傳入值或字典進行填充。
df?= pd.read_csv("data/weather_withna.csv")print(df.fillna(0).tail()) ?# 使用固定值填充# ?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
- 使用統計值填充
通過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
- 使用前后的有效值填充
通過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 ? ?rainprint(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
通過線性插值填充
通過interpolate()方法進行線性插值填充。線性插值操作,就是用于在已知數據點之間估算未知數據點的值。interpolate?方法支持多種插值方法,可通過?method?參數指定,常見的方法有:
- 'linear':線性插值,基于兩點之間的直線來估算缺失值,適用于數據呈線性變化的情況。
- 'time':適用于時間序列數據,會考慮時間間隔進行插值。
- 'polynomial':多項式插值,通過擬合多項式曲線來估算缺失值,可通過?order?參數指定多項式的階數。
import?pandas?as?pdimport?numpy?as?np# 創建包含缺失值的?Seriess?= 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
Bash
# 缺失值
import numpy as np
# 缺失值的類型?nan na
s = pd.Series([np.nan, None, pd.NA,2,4])
df = pd.DataFrame([[1, pd.NA, 2], [2, 3, 5], [pd.NA, 4, 6]])
print(s)
print(s.isnull()) ?#查看是否是缺失值
print(s.isna()) #查看是否是缺失值
print(s.isna().sum()) # 缺失值的個數
# 剔除缺失值
print(s.dropna()) ?#series剔除缺失值
print(df.dropna()) #只要有缺失值,就剔除一整條記錄
print(df.dropna(how="all")) # 如果所有值都是缺失值,則刪除這一行
print(df.dropna(thresh=2)) # 如果至少有2個值不是缺失值,則保留這一行
print(df.dropna(axis=1)) ?#剔除一列中含缺失值的列
#可以通過設置subset參數來設置某一列有缺失值則進行剔除。
print(df.dropna(subset=[0]))# 如果0列有缺失值,則刪除這一行
#填充缺失值
print('********')
df = pd.read_csv("data/weather_withna.csv")
# df = df.fillna({"temp_max": 60, "temp_min": -60}) # 使用字典來填充
print(df['temp_max'].mean())
df.fillna(df[["precipitation", "temp_max", "temp_min", "wind"]].mean()).tail() # 使用平均值填充
print(df.ffill().tail()) # 使用前面的有效值填充
print(df.bfill().tail()) # 使用后面的有效值填充df1 = pd.read_csv("data/weather_withna.csv")
df2 = pd.read_csv("data/weather_withna.csv", keep_default_na=False)
print(df1.temp_max.count())
print(df1.isnull().sum())
print(df2.temp_max.count())
print(df2.isnull().sum())
# 將
df = pd.read_csv("data/weather_withna.csv", na_values=["2015-12-31"])
# print(df.tail(5))
print(df.isnull().sum())
2. 重復數據處理
方法/操作 | 語法示例 | 描述 |
檢測重復行 | df.duplicated() | 返回布爾序列標記重復行(首次出現的行標記為False) |
刪除重復行 | df.drop_duplicates() | 保留首次出現的行(默認檢查所有列) |
df.drop_duplicates(subset=['col1']) | 僅根據指定列去重 | |
df.drop_duplicates(keep='last') | 保留最后一次出現的行 |
1. 檢測重復行
Python
import pandas as pd# 創建包含重復數據的DataFrame
data = {'Name': ['Alice', 'Bob', 'Alice', 'Charlie', 'Bob'],'Age': [25, 30, 25, 35, 30],'City': ['NY', 'LA', 'NY', 'SF', 'LA']
}
df = pd.DataFrame(data)# 檢測重復行(默認檢查所有列)
print("重復行標記(False表示首次出現,True表示重復):")
print(df.duplicated())
輸出:
Plain Text
0 ???False
1 ???False
2 ????True
3 ???False
4 ????True
dtype: bool
2. 刪除重復行
Python
# 默認保留首次出現的行
df_unique = df.drop_duplicates()
print("去重后的DataFrame:")
print(df_unique)
輸出:
Plain TextName ?Age City
0 ???Alice ??25 ??NY
1 ?????Bob ??30 ??LA
3 ?Charlie ??35 ??SF
3. 按指定列去重
Python
# 僅根據'Name'列去重(保留首次出現)
df_name_unique = df.drop_duplicates(subset=['Name'])
print("按Name列去重:")
print(df_name_unique)
輸出:
Plain TextName ?Age City
0 ???Alice ??25 ??NY
1 ?????Bob ??30 ??LA
3 ?Charlie ??35 ??SF
4. 保留最后一次出現的重復行
Python
# 保留最后一次出現的行
df_last = df.drop_duplicates(keep='last')
print("保留最后一次出現的行:")
print(df_last)
輸出:
Plain TextName ?Age City
2 ???Alice ??25 ??NY
4 ?????Bob ??30 ??LA
3 ?Charlie ??35 ??SF
5. 綜合案例:處理真實數據
Python
# 加載包含重復值的數據(示例)
df_sales = pd.read_csv("sales_data.csv")# 檢查重復行數量
print("原始數據重復行數:", df_sales.duplicated().sum())# 按'Order_ID'列去重,保留最后一次記錄
df_clean = df_sales.drop_duplicates(subset=['Order_ID'], keep='last')# 驗證結果
print("去重后數據行數:", len(df_clean))
注意事項
- 性能優化:對大數據集去重時,可通過?subset?指定關鍵列以減少計算量。
- 邏輯一致性:確保?keep='last'?或?keep=False(刪除所有重復)符合業務需求。
- 多列去重:subset=['col1', 'col2']?可聯合多列判斷重復。
通過以上案例,可以靈活應對實際數據清洗中的重復值問題!
3. ?數據類型轉換
方法/操作 | 語法示例 | 描述 |
查看數據類型 | df.dtypes | 顯示每列的數據類型 |
強制類型轉換 | df['col'].astype('int') | 將列轉換為指定類型(如int, float, str, datetime) |
轉換為日期時間 | pd.to_datetime(df['col']) | 將字符串列轉為datetime類型 |
轉換為分類數據 | df['col'].astype('category') | 將列轉為分類類型(節省內存,提高性能) |
數值格式化 | df['col'].round(2) | 保留2位小數 |
核心方法
操作 | 方法/函數 | 描述 |
查看數據類型 | df.dtypes | 顯示每列的數據類型(如int64、float64、object等)。 |
強制類型轉換 | df['col'].astype('type') | 將列轉換為指定類型(如int、float、str、bool等)。 |
轉換為日期時間 | pd.to_datetime(df['col']) | 將字符串或數值列轉為datetime類型(支持自定義格式)。 |
轉換為分類數據 | df['col'].astype('category') | 將列轉為分類類型(節省內存,提高性能,適用于有限取值的列如性別、省份)。 |
數值格式化 | df['col'].round(2) | 保留指定小數位數(如2位)。 |
代碼案例講解
1. 查看數據類型
Python
import pandas as pd# 加載數據(以sleep.csv為例)
df = pd.read_csv("sleep.csv")
print(df.dtypes)
輸出示例:
Plain Text
person_id ????????????????????int64
gender ??????????????????????object
age ??????????????????????????int64
occupation ??????????????????object
sleep_duration ?????????????float64
sleep_quality ??????????????float64
... ?????????????????????????...
說明:object通常為字符串或混合類型,需檢查是否需要轉換。 |
2. 強制類型轉換
將數值列轉換為整數或字符串:
Python
# 將sleep_duration從float轉為int(丟失小數部分)
df['sleep_duration_int'] = df['sleep_duration'].astype('int32')# 將gender轉為字符串
df['gender_str'] = df['gender'].astype('str')print(df[['sleep_duration', 'sleep_duration_int', 'gender_str']].head())
輸出:
Plain Text |
3. 轉換為日期時間
處理時間數據(假設employees.csv有日期列):
Python
# 示例:創建臨時日期列(實際數據可能為hire_date)
df_employees = pd.read_csv("employees.csv")
df_employees['fake_date'] = '2023-01-' + df_employees['employee_id'].astype(str).str[:2]# 轉換為datetime
df_employees['fake_date'] = pd.to_datetime(df_employees['fake_date'])
print(df_employees[['employee_id', 'fake_date']].head())
輸出:
Plain Textemployee_id ?fake_date
0 ?????????100 2023-01-10
1 ?????????101 2023-01-10
2 ?????????102 2023-01-10
注意:若原始格式非標準,需指定格式參數,如:?? pd.to_datetime(df['date'], format='%Y/%m/%d') |
4. 轉換為分類數據
優化內存和性能(適用于低基數列):
Python
# 將gender列轉為分類類型
df['gender'] = df['gender'].astype('category')
print(df['gender'].dtypes)
輸出:
|
優勢:??
|
5. 數值格式化
控制小數位數:
|
輸出:
|
常見問題與技巧
- 處理轉換錯誤:使用errors='coerce'將無效值轉為NaN,避免報錯:
|
- 內存優化:將數值列從int64轉為int32或float32:
|
- 布爾類型轉換:將字符串(如"Yes"/"No")轉為布爾值:
|
- 自定義格式化:使用apply實現復雜轉換(如百分比):
|
實戰案例:處理penguins.csv
|
輸出:
Plain Text |
4. ?數據重塑與變形
方法/操作 | 語法示例 | 描述 |
行列轉置 | df.T | 轉置DataFrame(行變列,列變行) |
寬表轉長表 | pd.melt(df, id_vars=['id']) | 將多列合并為鍵值對形式(variable和value列) |
長表轉寬表 | df.pivot(index='id', columns='var', values='val') | 將長表轉換為寬表(類似Excel數據透視) |
分列操作 | df['col'].str.split(',', expand=True) | 按分隔符拆分字符串為多列 |
1. 行列轉置(df.T)
將DataFrame的行列互換,適用于需要橫向展示數據的場景。
Python |
輸出:
Plain Text |
2. 寬表轉長表(**pd.melt()**)
將多列合并為鍵值對形式,適合分析多指標數據。
Python |
輸出:
Plain Text |
3. 長表轉寬表(**df.pivot()**)
將長表轉換為寬表,類似Excel的數據透視表。
Python |
輸出:
Plain Text |
4. 分列操作(**str.split()**)
按分隔符拆分字符串列,生成多列。
Python |
輸出:
Plain Text |
注意事項
- pivot**與****pivot_table****的區別**:??
- pivot要求索引和列的組合唯一,否則報錯。??
- pivot_table支持聚合(如均值、求和),適合非唯一組合。
- 分列操作:??
- 使用expand=True將拆分結果轉為多列。??
- 若分隔符數量不一致,需預處理數據(如填充缺失值)。
- 內存管理:??
- 寬表轉長表可能增加行數,需注意內存占用。
Bash |
5. ??文本數據處理
方法/操作 | 語法示例 | 描述 |
字符串大小寫轉換 | df['col'].str.lower() | 轉為小寫 |
去除空格 | df['col'].str.strip() | 去除兩端空格 |
字符串替換 | df['col'].str.replace('old', 'new') | 替換文本 |
正則表達式提取 | df['col'].str.extract(r'(\d+)') | 提取匹配正則的文本(如數字) |
字符串包含檢測 | df['col'].str.contains('abc') | 返回布爾序列,判斷是否包含子串 |
1. 字符串大小寫轉換
統一文本格式,便于后續分析(如姓名、地址等)。
Python |
輸出:
Plain Text |
2. 去除空格
處理用戶輸入或爬取數據中的多余空格。
Python |
輸出:
Plain Text |
3. 字符串替換
替換文本中的特定字符或模式(如清理臟數據)。
Python |
輸出:
Plain Text |
4. 正則表達式提取
從文本中提取結構化信息(如電話號碼、日期)。
Python |
輸出:
Plain Text |
5. 字符串包含檢測
篩選包含特定關鍵詞的記錄。
Python |
輸出:
Plain Text |
實戰案例:處理employees.csv
清理員工姓名和郵箱數據:
Python |
輸出:
Plain Text |
6. ?數據分箱與離散化
方法/操作 | 語法示例 | 描述 |
等寬分箱 | pd.cut(df['col'], bins=3) | 將數值列分為等寬區間(如分為低/中/高) |
等頻分箱 | pd.qcut(df['col'], q=4) | 將數值列分為等頻區間(每箱數據量相同) |
cut()
pandas.cut()用于將連續數據(如數值型數據)分割成離散的區間。可以使用cut()來將數據劃分為不同的類別或范圍,通常用于數據的分箱處理。
cut()部分參數說明:
參數 | 說明 |
x | 要分箱的數組或Series,通常是數值型數據。 |
bins | 切分區間的數值列表或者整數。如果是整數,則表示將數據均勻地分成多少個區間。如果是列表,則需要指定每個區間的邊界。 |
right | 默認True,表示每個區間的右端點是閉區間,即包含右端點。如果設置為False,則左端點為閉區間。 |
labels | 傳入一個列表指定每個區間的標簽。 |
Bash |
7. ?其他常用轉換
- df.rename(columns={"score": "成績"})
- df.set_index("name")
- df.reset_index()
Python |
3.4 數據的導入與導出
導出數據
方法 | 說明 |
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() | 保存到數據庫。 |
Python |
導入數據
方法 | 說明 |
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() | 加載數據庫中的數據。 |
Python |
3.6 時間數據的處理
Timestamp 是?pandas 對?datetime64 數據類型的一個封裝。datetime64 是?NumPy 中的一種數據類型,用于表示日期和時間,而?pandas 基于?datetime64 構建了?Timestamp 類,以便更方便地在?pandas 的數據結構(如?DataFrame 和?Series)中處理日期時間數據。當?pd.to_datetime 接收單個日期時間值時,會返回?Timestamp 對象
1. 時間戳timestamp
Python |
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?。
2. ?日期數據轉換
Bash |
Bash |
3. 將日期數據作為索引
將datetime64類型的數據設置為索引,得到的就是DatetimeIndex。
Bash |
將時間作為索引后可以直接使用時間進行切片取值。
Plain Text |
也可以通過between_time()和at_time()獲取某些時刻的數據。
Plain Text |
4. 時間間隔timedelta
當用一個日期減去另一個日期,返回的結果是timedelta64類型。
Plain Text |
TimedeltaIndex
將timedelta64類型的數據設置為索引,得到的就是TimedeltaIndex。
Plain Text |
將時間作為索引后可以直接使用時間進行切片取值。
Plain Text |
5. 時間序列
生成時間序列
為了能更簡便地創建有規律的時間序列,pandas提供了date_range()方法。
date_range()通過開始日期、結束日期和頻率代碼(可選)創建一個有規律的日期序列,默認的頻率是天。
Python |
此外,日期范圍不一定非是開始時間與結束時間,也可以是開始時間與周期數periods。
Plain Text |
可以通過freq參數設置時間頻率,默認值是D。此處改為h,按小時變化的時間戳。
Plain Text |
下表為常見時間頻率代碼與說明:
代碼 | 說明 |
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) |
偏移量
可以在頻率代碼后面加三位月份縮寫字母來改變季、年頻率的開始時間。
- QE-JAN、BQE-FEB、QS-MAR、BQS-APR等
- YE-JAN、BYE-FEB、YS-MAR、BYS-APR等
Shell |
同理,也可以在后面加三位星期縮寫字母來改變一周的開始時間。
- W-SUN、W-MON、W-TUE、W-WED等
Shell |
在這些代碼的基礎上,還可以將頻率組合起來創建的新的周期。例如,可以用小時(h)和分鐘(min)的組合來實現2小時30分鐘。
Shell |
6. 重新采樣
處理時間序列數據時,經常需要按照新的頻率(更高頻率、更低頻率)對數據進行重新采樣。可以通過resample()方法解決這個問題。resample()方法以數據累計為基礎,會將數據按指定的時間周期進行分組,之后可以對其使用聚合函數。
Bash |
3.7 數據分析與統計
分類 | 依賴關系 | 協同應用場景 | 示例 |
描述性統計 | 所有分析的基礎 | 初步了解數據分布,指導后續分組策略 | df.describe()?發現某列標準差大?→ 觸發分組過濾 |
分組聚合 | 基于描述性統計或分組過濾結果 | 按維度拆分后計算指標(如各品類銷售額總和) | df.groupby('category')['sales'].sum() |
分組轉換 | 依賴分組聚合結構 | 在保留原始行數的前提下,添加組內計算列(如標準化、排名) | df.groupby('group')['value'].transform(lambda x: x/x.max()) |
分組過濾 | 依賴描述性統計或分組聚合結果 | 根據組級條件篩選數據(如剔除樣本量不足的組) | df.groupby('group').filter(lambda x: len(x) > 5) |
相關性分析 | 可結合分組聚合結果 | 分析不同分組下變量的關聯性(如各地區的價格-銷量相關性) | df.groupby('region')[['price','sales']].corr() |
關鍵交互邏輯
- 從宏觀到微觀??
- 描述性統計(宏觀)?→ 分組聚合(細分維度)?→ 分組轉換/過濾(微觀調整)
- 數據流閉環??
Python |
- 功能互補性??
- 聚合?vs 轉換:聚合減少行數,轉換保持行數。??
- 過濾?vs 轉*:過濾刪除整組,轉換修改組內值。
可視化應用場景
通過以上關系圖和表格,可清晰理解如何組合這些方法解決實際問題,例如:??
- 數據清洗:描述統計?→ 發現異常?→ 分組過濾??
- 特征工程:分組聚合?→ 分組轉換(如生成占比特征)??
- 業務分析:分組聚合?→ 相關性分析(如用戶分群行為關聯)
1. 常用聚合函數
方法 | 說明 |
sum() | 求和 |
mean() | 平均值 |
min() | 最小值 |
max() | 最大值 |
var() | 方差 |
std() | 標準差 |
median() | 中位數 |
quantile() | 指定位置的分位數,如quantile(0.5) |
describe() | 常見統計信息 |
size() | 所有元素的個數 |
count() | 非空元素的個數 |
first | 第一行 |
last | 最后一行 |
nth | 第n行 |
2. 分組聚合
df.groupby("分組字段")["要聚合的字段"].聚合函數()
df.groupby(["分組字段", "分組字段2", ...])[["要聚合的字段", "要聚合的字段2", ...]].聚合函數()
DataFrameGroupBy對象
對DataFrame對象調用groupby()方法后,會返回DataFrameGroupBy對象。
Python |
這個對象可以看成是一種特殊形式的?DataFrame,里面隱藏著若干組數據,但是在沒有應用累計函數之前不會計算。GroupBy對象是一種非常靈活的抽象類型。在大多數場景中,可以將它看成是DataFrame的集合。
查看分組
通過groups屬性查看分組結果,返回一個字典,字典的鍵是分組的標簽,值是屬于該組的所有索引的列表。
Python |
通過get_group()方法獲取分組。
Python |
按列取值
Python |
這里從原來的DataFrame中取某個列名作為一個Series組。與GroupBy對象一樣,直到我們運行累計函數,才會開始計算。
Python |
按組迭代
GroupBy對象支持直接按組進行迭代,返回的每一組都是Series或DataFrame。
Python |
...
按多字段分組
Python |
按多個字段分組后得到的索引為復合索引。
可通過reset_index()方法重置索引。
Python |
也可以在分組的時候通過as_index = False參數(默認是True)重置索引。
Python |
將數據按月分組,并統計最大溫度和最小溫度的平均值
Python |
分組后默認會將分組字段作為行索引。如果分組字段有多個,得到的是復合索引。
分組頻數計算
統計每個月不同天氣狀況的數量。
Python |
3. 一次計算多個統計值
可以通過agg()或aggregate()進行更復雜的操作,如一次計算多個統計值。
Python |
多個列計算不同的統計值
也可以在agg()中傳入字典,對多個列計算不同的統計值。
Python |
重命名統計值
可以在agg()后通過rename()對統計后的列重命名。
Python |
自定義函數
可以向agg()中傳入自定義函數進行計算。
Python |
4. 分組轉換
聚合操作返回的是對組內全量數據縮減過的結果,而轉換操作會返回一個新的全量數據。數據經過轉換之后,其形狀與原來的輸入數據是一樣的。
通過transform()將每一組的樣本數據減去各組的均值,實現數據標準化
df?= pd.read_csv("data/employees.csv") ?# 讀取員工數據
print(df.groupby("department_id")["salary"].transform(lambda?x: x?- x.mean()))
通過transform()按分組使用平均值填充缺失值
Python |
5. 分組過濾
過濾操作可以讓我們按照分組的屬性丟棄若干數據。
例如,我們可能只需要保留commission_pct不包含空值的分組的數據。
Python |
3.8 案例講解
Python |
Python
# 導入必要的庫
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns# 設置可視化風格
plt.style.use('seaborn')
sns.set_palette("husl")# 1. 數據加載與初步探索
# 讀取睡眠數據集
sleep_data = pd.read_csv('sleep.csv')# 顯示前5行數據
print("數據集前5行:")
print(sleep_data.head())# 顯示數據集基本信息
print("\n數據集信息:")
print(sleep_data.info())# 2. 數據清洗與預處理
# 檢查缺失值
print("\n每列的缺失值數量:")
print(sleep_data.isnull().sum())# 如果有缺失值,可以刪除或填充
# 這里假設數據已經完整,直接復制
sleep_clean = sleep_data.copy()# 3. 數據轉換與特征工程
# 將性別列轉換為類別類型
sleep_clean['gender'] = sleep_clean['gender'].astype('category')# 分離血壓為收縮壓和舒張壓
sleep_clean[['systolic_bp', 'diastolic_bp']] = sleep_clean['blood_pressure'].str.split('/', expand=True).astype(int)# 創建睡眠質量分類列
bins = [0, 4, 7, 10]
labels = ['差', '中', '優']
sleep_clean['sleep_quality_category'] = pd.cut(sleep_clean['sleep_quality'], bins=bins, labels=labels)# 創建年齡分段列
age_bins = [0, 30, 45, 60, 100]
age_labels = ['18-30', '31-45', '46-60', '60+']
sleep_clean['age_group'] = pd.cut(sleep_clean['age'], bins=age_bins, labels=age_labels)# 4. 基本統計分析
# 描述性統計
print("\n數值變量的描述性統計:")
print(sleep_clean.describe())# 分類變量統計
print("\n分類變量統計:")
print(sleep_clean['gender'].value_counts())
print("\nBMI類別分布:")
print(sleep_clean['bmi_category'].value_counts())
print("\n睡眠障礙分布:")
print(sleep_clean['sleep_disorder'].value_counts())# 5. 睡眠質量分析
# 按性別分析平均睡眠時間和質量
gender_stats = sleep_clean.groupby('gender').agg({'sleep_duration': 'mean','sleep_quality': 'mean','stress_level': 'mean'
}).round(2)
print("\n按性別分組的睡眠統計:")
print(gender_stats)# 按BMI類別分析
bmi_stats = sleep_clean.groupby('bmi_category').agg({'sleep_duration': 'mean','sleep_quality': 'mean','physical_activity_level': 'mean'
}).round(2)
print("\n按BMI類別分組的睡眠統計:")
print(bmi_stats)# 6. 睡眠障礙分析
# 有睡眠障礙和無睡眠障礙的比較
disorder_stats = sleep_clean.groupby('sleep_disorder').agg({'sleep_duration': ['mean', 'count'],'sleep_quality': 'mean','age': 'mean','stress_level': 'mean'
}).round(2)
print("\n按睡眠障礙分組的統計:")
print(disorder_stats)# 7. 相關性分析
# 計算數值變量之間的相關性
correlation = sleep_clean[['sleep_duration', 'sleep_quality', 'age', 'physical_activity_level','stress_level', 'heart_rate', 'daily_steps', 'systolic_bp', 'diastolic_bp']].corr()print("\n變量間相關性矩陣:")
print(correlation)# 8. 高級分析?- 多因素分組
# 按性別和年齡組分析
gender_age_stats = sleep_clean.groupby(['gender', 'age_group']).agg({'sleep_duration': 'mean','sleep_quality': 'mean','stress_level': 'mean'
}).round(2)
print("\n按性別和年齡組分組的統計:")
print(gender_age_stats)# 按職業和BMI類別分析
occupation_bmi_stats = sleep_clean.groupby(['occupation', 'bmi_category']).agg({'sleep_duration': 'mean','sleep_quality': 'mean'
}).round(2)
print("\n按職業和BMI類別分組的統計:")
print(occupation_bmi_stats)# 9. 數據可視化
# 設置圖形大小
plt.figure(figsize=(15, 10))# 睡眠質量分布
plt.subplot(2, 2, 1)
sns.histplot(sleep_clean['sleep_quality'], bins=10, kde=True)
plt.title('睡眠質量分布')
plt.xlabel('睡眠質量評分')
plt.ylabel('人數')# 睡眠持續時間分布
plt.subplot(2, 2, 2)
sns.histplot(sleep_clean['sleep_duration'], bins=10, kde=True)
plt.title('睡眠持續時間分布')
plt.xlabel('睡眠時間(小時)')
plt.ylabel('人數')# 睡眠質量與壓力水平的關系
plt.subplot(2, 2, 3)
sns.scatterplot(x='stress_level', y='sleep_quality', hue='gender', data=sleep_clean)
plt.title('睡眠質量與壓力水平的關系')
plt.xlabel('壓力水平')
plt.ylabel('睡眠質量')# 不同BMI類別的平均睡眠質量
plt.subplot(2, 2, 4)
sns.barplot(x='bmi_category', y='sleep_quality', data=sleep_clean, ci=None)
plt.title('不同BMI類別的平均睡眠質量')
plt.xlabel('BMI類別')
plt.ylabel('平均睡眠質量')plt.tight_layout()
plt.show()# 10. 更多可視化
plt.figure(figsize=(15, 5))# 按年齡組的睡眠質量
plt.subplot(1, 2, 1)
sns.boxplot(x='age_group', y='sleep_quality', hue='gender', data=sleep_clean)
plt.title('不同年齡組的睡眠質量')
plt.xlabel('年齡組')
plt.ylabel('睡眠質量')# 睡眠障礙與睡眠質量的關系
plt.subplot(1, 2, 2)
sns.boxplot(x='sleep_disorder', y='sleep_quality', data=sleep_clean)
plt.title('睡眠障礙與睡眠質量的關系')
plt.xlabel('睡眠障礙類型')
plt.ylabel('睡眠質量')plt.tight_layout()
plt.show()# 11. 保存處理后的數據
sleep_clean.to_csv('cleaned_sleep_data.csv', index=False)
print("\n處理后的數據已保存為?cleaned_sleep_data.csv")