自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Pandas 進(jìn)階秘籍:五招搞定復(fù)雜數(shù)據(jù)處理,效率飆升!

大數(shù)據(jù) 開發(fā)
本文將深入探討五個(gè) Pandas 進(jìn)階實(shí)用技巧,助您優(yōu)雅地應(yīng)對(duì)復(fù)雜數(shù)據(jù)處理挑戰(zhàn),顯著提升代碼質(zhì)量和運(yùn)行效率。

Pandas 是 Python 數(shù)據(jù)分析領(lǐng)域當(dāng)之無愧的瑞士軍刀,其強(qiáng)大的數(shù)據(jù)結(jié)構(gòu) DataFrame 和 Series 使得數(shù)據(jù)清洗、轉(zhuǎn)換、分析變得高效便捷。然而,當(dāng)面對(duì)日益復(fù)雜的數(shù)據(jù)場(chǎng)景和性能要求時(shí),僅僅掌握基礎(chǔ)操作往往捉襟見肘。

本文將深入探討 5 個(gè) Pandas 進(jìn)階實(shí)用技巧,助您優(yōu)雅地應(yīng)對(duì)復(fù)雜數(shù)據(jù)處理挑戰(zhàn),顯著提升代碼質(zhì)量和運(yùn)行效率。

一、鏈?zhǔn)讲僮?(Method Chaining):優(yōu)雅的數(shù)據(jù)流

鏈?zhǔn)讲僮?,也稱為流式接口 (Fluent Interface),是將一系列 Pandas 方法串聯(lián)起來,形成一個(gè)清晰、連貫的數(shù)據(jù)處理管道。這種風(fēng)格不僅代碼更易讀、易維護(hù),還能有效避免創(chuàng)建不必要的中間變量,優(yōu)化內(nèi)存使用。

1. 為什么選擇鏈?zhǔn)讲僮鳎?/h4>
  • 可讀性強(qiáng):代碼從上到下或從左到右,清晰地展示了數(shù)據(jù)轉(zhuǎn)換的每一步。
  • 簡(jiǎn)潔性高:減少了中間變量的聲明,使代碼更加緊湊。
  • 易于調(diào)試:雖然長(zhǎng)鏈可能看起來復(fù)雜,但可以通過逐個(gè)添加方法或使用 pipe() 方法(見后文)進(jìn)行調(diào)試。
  • 性能潛力:在某些情況下,Pandas 內(nèi)部可能對(duì)鏈?zhǔn)讲僮鬟M(jìn)行優(yōu)化。

2. 示例:傳統(tǒng)方式 vs. 鏈?zhǔn)讲僮?/h4>

假設(shè)我們需要對(duì)一個(gè)銷售數(shù)據(jù) DataFrame (df) 進(jìn)行如下操作:

  • 篩選出 'Category' 為 'Electronics' 的記錄。
  • 按 'Sub-Category' 分組。
  • 計(jì)算每個(gè)子類的總銷售額 ('Sales') 和平均利潤(rùn) ('Profit')。
  • 按總銷售額降序排序。
  • 取銷售額最高的前 5 個(gè)子類。

(1) 傳統(tǒng)方式 (創(chuàng)建中間變量):

# 假設(shè) df 是已加載的銷售數(shù)據(jù) DataFrame
electronics_df = df[df['Category'] == 'Electronics']
grouped_df = electronics_df.groupby('Sub-Category')
agg_df = grouped_df.agg(
    TotalSales=('Sales', 'sum'),
    AverageProfit=('Profit', 'mean')
)
sorted_df = agg_df.sort_values(by='TotalSales', ascending=False)
top_5_subcategories = sorted_df.head(5)
print(top_5_subcategories)

(2) 鏈?zhǔn)讲僮?

# 假設(shè) df 是已加載的銷售數(shù)據(jù) DataFrame
top_5_subcategories_chained = (
    df[df['Category'] == 'Electronics']  # 1. 篩選
    .groupby('Sub-Category')             # 2. 分組
    .agg(                                # 3. 聚合
        TotalSales=('Sales', 'sum'),
        AverageProfit=('Profit', 'mean')
    )
    .sort_values(by='TotalSales', ascending=False) # 4. 排序
    .head(5)                             # 5. 取前5
)
print(top_5_subcategories_chained)

注釋:鏈?zhǔn)讲僮魍ㄟ^將每個(gè)方法調(diào)用的結(jié)果直接作為下一個(gè)方法調(diào)用的對(duì)象,形成了一個(gè)流暢的管道。使用圓括號(hào) () 包裹整個(gè)鏈條可以方便地進(jìn)行多行書寫,提高可讀性。

3. 提升鏈?zhǔn)讲僮骺勺x性的技巧

  • 適當(dāng)換行和縮進(jìn):如上例所示,每個(gè) .method() 調(diào)用占一行。
  • 添加注釋:在每個(gè)步驟后添加簡(jiǎn)短注釋說明其作用。
  • 使用 pipe() 方法:對(duì)于需要傳遞 DataFrame 給自定義函數(shù)或不易直接鏈?zhǔn)秸{(diào)用的函數(shù),pipe() 非常有用(詳見技巧二)。

二、pipe() 方法:自定義函數(shù)的無縫融入

當(dāng)鏈?zhǔn)讲僮髦行枰獞?yīng)用一個(gè)自定義函數(shù),或者某個(gè)庫函數(shù)不直接支持在 DataFrame/Series 對(duì)象上調(diào)用時(shí),pipe() 方法就派上了用場(chǎng)。它允許你將 DataFrame 或 Series 作為第一個(gè)參數(shù)傳遞給指定的函數(shù),并將函數(shù)的返回值作為 pipe() 的結(jié)果,從而保持鏈?zhǔn)讲僮鞯牧鲿承浴?/p>

1. pipe() 的優(yōu)勢(shì)

  • 保持鏈?zhǔn)浇Y(jié)構(gòu):即使需要調(diào)用外部函數(shù),也能維持代碼的流式風(fēng)格。
  • 封裝復(fù)雜邏輯:可以將復(fù)雜的數(shù)據(jù)處理步驟封裝在自定義函數(shù)中,然后通過 pipe() 調(diào)用。
  • 傳遞額外參數(shù):pipe() 允許向目標(biāo)函數(shù)傳遞額外的參數(shù)。

2. 示例:使用 pipe() 進(jìn)行自定義數(shù)據(jù)清洗

假設(shè)我們有一個(gè)自定義函數(shù) clean_text_column(df, column_name) 用于清洗 DataFrame 中的某個(gè)文本列(例如轉(zhuǎn)換為小寫、去除特殊字符)。

import pandas as pd
import re

# 示例 DataFrame
data = {'ID': [1, 2, 3],
        'Description': ['Product A - NEW!', 'Item B (Old Model)', 'Widget C*']}
df_text = pd.DataFrame(data)

# 自定義清洗函數(shù)
def clean_text_column(dataframe, column_to_clean, remove_chars_pattern=r'[^a-zA-Z0-9\s]'):
    """清洗指定文本列:轉(zhuǎn)小寫,移除特定字符"""
    df_copy = dataframe.copy() # 避免修改原始 DataFrame
    df_copy[column_to_clean] = (
        df_copy[column_to_clean]
        .str.lower() # 轉(zhuǎn)小寫
        .str.replace(remove_chars_pattern, '', regex=True) # 移除特定字符
        .str.strip() # 去除首尾空格
    )
    return df_copy

# 使用 pipe() 調(diào)用自定義函數(shù)
cleaned_df = (
    df_text
    .pipe(clean_text_column, column_to_clean='Description') # 將 df_text 作為第一個(gè)參數(shù)傳遞
    # 可以在這里繼續(xù)鏈接其他 Pandas 操作
    # .assign(DescriptionLength=lambda x: x['Description'].str.len())
)
print(cleaned_df)

注釋:df_text.pipe(clean_text_column, column_to_clean='Description') 實(shí)際上等同于 clean_text_column(df_text, column_to_clean='Description'),但它允許我們將其嵌入到鏈?zhǔn)讲僮髦?。column_to_clean='Description' 是傳遞給 clean_text_column 函數(shù)的額外命名參數(shù)。

三、explode() 與 stack()/unstack():處理復(fù)雜結(jié)構(gòu)數(shù)據(jù)

當(dāng) DataFrame 中的某一列包含列表、元組或其他可迭代對(duì)象,或者當(dāng)需要重塑多級(jí)索引的數(shù)據(jù)時(shí),explode()、stack() 和 unstack() 方法非常有用。

1. explode():將列表式數(shù)據(jù)展開為多行

如果 DataFrame 的某一列包含列表或類似列表的條目,而你希望將每個(gè)列表元素?cái)U(kuò)展成單獨(dú)的行,保留其他列的值,explode() 是理想選擇。

示例;

data_explode = {'OrderID': [1, 2],
                'Products': [['Apple', 'Banana'], ['Orange', 'Grape', 'Apple']]}
df_explode_before = pd.DataFrame(data_explode)
print("原始 DataFrame:\n", df_explode_before)

# 使用 explode() 展開 'Products' 列
df_exploded = df_explode_before.explode('Products').reset_index(drop=True)
print("\nExplode 之后:\n", df_exploded)

注釋:explode('Products') 將 Products 列中的每個(gè)列表元素拆分成新行,OrderID 的值會(huì)相應(yīng)復(fù)制。reset_index(drop=True) 用于重置索引,避免因展開產(chǎn)生重復(fù)索引。

2. stack() 與 unstack():重塑多級(jí)索引

stack() 和 unstack() 主要用于處理具有多級(jí)索引 (MultiIndex) 的 DataFrame,在寬格式 (wide format) 和長(zhǎng)格式 (long format) 數(shù)據(jù)之間進(jìn)行轉(zhuǎn)換。

  • stack(): 將 DataFrame 的列“堆疊”到索引中,通常會(huì)使 DataFrame 變得更“長(zhǎng)”(行數(shù)增加,列數(shù)減少)。
  • unstack(): stack() 的逆操作,將索引的某個(gè)級(jí)別“解堆”到列中,通常使 DataFrame 變得更“寬”。

示例:

# 創(chuàng)建一個(gè)帶有多級(jí)列索引的 DataFrame
header = pd.MultiIndex.from_product([['Year1', 'Year2'], ['Sales', 'Profit']])
data_multiindex = [[100, 10, 120, 15], [150, 20, 130, 18]]
df_wide = pd.DataFrame(data_multiindex, index=['StoreA', 'StoreB'], columns=header)
print("原始寬格式 DataFrame (多級(jí)列索引):\n", df_wide)

# 使用 stack() 將最內(nèi)層的列索引級(jí)別 (Sales, Profit) 堆疊到行索引
df_long_stacked = df_wide.stack(level=-1) # level=-1 表示最內(nèi)層列索引
print("\nStack 之后 (長(zhǎng)格式):\n", df_long_stacked)

# 使用 unstack() 將剛剛堆疊的級(jí)別解堆回列
df_unstacked_back = df_long_stacked.unstack(level=-1) # level=-1 表示最內(nèi)層行索引
print("\nUnstack 回去:\n", df_unstacked_back)

注釋:stack() 和 unstack() 的 level 參數(shù)指定了要操作的索引級(jí)別(可以是名稱或整數(shù)位置)。這些操作對(duì)于時(shí)間序列分析、面板數(shù)據(jù)處理以及準(zhǔn)備用于特定可視化或統(tǒng)計(jì)模型的數(shù)據(jù)非常關(guān)鍵。

四、assign():動(dòng)態(tài)創(chuàng)建新列的利器

在鏈?zhǔn)讲僮髦?,如果需要基于現(xiàn)有列計(jì)算并添加新列,assign() 方法提供了一種非常優(yōu)雅和函數(shù)式的方式。它返回一個(gè)新的 DataFrame,包含原始列和新添加的列,而不會(huì)修改原始 DataFrame。

1. assign() 的特點(diǎn)

  • 鏈?zhǔn)接押茫和昝廊谌腈準(zhǔn)讲僮鳌?/li>
  • 函數(shù)式編程風(fēng)格:可以使用 lambda 函數(shù)動(dòng)態(tài)定義新列的計(jì)算邏輯。
  • 可讀性高:新列的名稱和計(jì)算方式一目了然。
  • 創(chuàng)建多個(gè)列:可以一次性創(chuàng)建多個(gè)新列。

2. 示例:使用 assign() 計(jì)算衍生指標(biāo)

data_assign = {'Item': ['A', 'B'], 'Price': [10, 20], 'Quantity': [5, 3]}
df_sales = pd.DataFrame(data_assign)

df_with_revenue = (
    df_sales
    .assign(
        Revenue=lambda x: x['Price'] * x['Quantity'], # 新列 Revenue
        DiscountedPrice=lambda x: x['Price'] * 0.9   # 新列 DiscountedPrice
    )
    .assign(
        # 可以在后續(xù)的 assign 中使用前面 assign 創(chuàng)建的列
        FinalRevenue=lambda x: x['DiscountedPrice'] * x['Quantity']
    )
)
print(df_with_revenue)

注釋:assign() 的參數(shù)是 新列名=計(jì)算邏輯。計(jì)算邏輯可以是一個(gè)標(biāo)量、一個(gè) Series,或者最常用的是一個(gè)接受 DataFrame (通常用 x 或 df_ 表示) 并返回新列值的 lambda 函數(shù)。Lambda 函數(shù)中的 x 代表調(diào)用 assign() 的那個(gè)時(shí)刻的 DataFrame 狀態(tài)。

五、優(yōu)化性能:eval() 與 query(),以及數(shù)據(jù)類型選擇

當(dāng)處理大型 DataFrame 時(shí),性能成為關(guān)鍵考量。Pandas 提供了一些方法來優(yōu)化表達(dá)式計(jì)算和數(shù)據(jù)篩選,同時(shí)合理選擇數(shù)據(jù)類型也能顯著提升效率和減少內(nèi)存占用。

1. eval() 和 query():高效表達(dá)式計(jì)算與篩選

  • df.eval(expression_string): 使用 Numexpr 庫(如果已安裝)或 Python 的 eval() 來高效計(jì)算字符串形式的列表達(dá)式,尤其在涉及多個(gè)列的算術(shù)運(yùn)算時(shí),可以避免生成大量中間 Series,從而節(jié)省內(nèi)存和提高速度。
  • df.query(expression_string): 同樣使用 Numexpr 進(jìn)行布爾表達(dá)式的求值,用于高效篩選行,語法比傳統(tǒng)的布爾索引更簡(jiǎn)潔。

示例:

# 創(chuàng)建一個(gè)較大的示例 DataFrame
import numpy as np
size = 100000
df_large = pd.DataFrame({
    'A': np.random.rand(size),
    'B': np.random.rand(size),
    'C': np.random.rand(size),
    'D': np.random.randint(0, 10, size)
})

# 使用 eval() 計(jì)算新列
df_large['E_eval'] = df_large.eval('A + B * C - D/2')

# 傳統(tǒng)方式計(jì)算新列 (可能較慢且占用更多內(nèi)存)
# df_large['E_traditional'] = df_large['A'] + df_large['B'] * df_large['C'] - df_large['D']/2

# 使用 query() 進(jìn)行篩選
threshold_a = 0.5
threshold_d = 5
# 可以在表達(dá)式字符串中通過 @ 符號(hào)引用外部變量
filtered_df_query = df_large.query('A > @threshold_a and D < @threshold_d and B > C')

# 傳統(tǒng)方式篩選 (可能較冗長(zhǎng))
# filtered_df_traditional = df_large[
#     (df_large['A'] > threshold_a) &
#     (df_large['D'] < threshold_d) &
#     (df_large['B'] > df_large['C'])
# ]

print("使用 eval 計(jì)算的 E 列的前5行:\n", df_large[['E_eval']].head())
print("\n使用 query 篩選后的 DataFrame 形狀:", filtered_df_query.shape)

注釋:eval() 和 query() 的表達(dá)式是字符串。對(duì)于 query(),可以使用 @variable_name 的方式在查詢字符串中引用 Python 環(huán)境中的變量。這些方法在 DataFrame 較大時(shí)性能優(yōu)勢(shì)更明顯。

2. 合理選擇數(shù)據(jù)類型 (dtypes)

Pandas 默認(rèn)的數(shù)據(jù)類型(如 int64, float64, object)可能不是最優(yōu)的,尤其對(duì)于內(nèi)存有限或追求極致性能的場(chǎng)景。

  • 數(shù)值類型:如果整數(shù)范圍較小,可以使用 int8, int16, int32 代替 int64。浮點(diǎn)數(shù)類似,float32 代替 float64。
  • **分類類型 (Category)**:對(duì)于基數(shù)(唯一值數(shù)量)遠(yuǎn)小于數(shù)據(jù)總長(zhǎng)度的字符串列(如性別、國(guó)家、產(chǎn)品類別),將其轉(zhuǎn)換為 category 類型可以大幅減少內(nèi)存占用并加快分組等操作。
  • **日期時(shí)間類型 (Datetime)**:確保日期時(shí)間字符串被正確解析為 datetime64[ns] 類型,以便利用 Pandas 強(qiáng)大的日期時(shí)間功能。

示例:轉(zhuǎn)換數(shù)據(jù)類型

# 假設(shè) df_large 是上面創(chuàng)建的 DataFrame
print("\n原始數(shù)據(jù)類型及內(nèi)存占用:")
df_large.info(memory_usage='deep')

# 轉(zhuǎn)換 'D' 列為更小的整數(shù)類型 (如果適用)
# 先檢查 D 的取值范圍
# print(df_large['D'].min(), df_large['D'].max()) # 0-9,int8 足夠
df_large['D_optimized'] = df_large['D'].astype('int8')

# 假設(shè) 'Category_String' 是一列基數(shù)較低的字符串
df_large['Category_String'] = pd.Series(np.random.choice(['X', 'Y', 'Z'], size=size, p=[0.6, 0.3, 0.1]))
df_large['Category_Optimized'] = df_large['Category_String'].astype('category')

print("\n優(yōu)化后數(shù)據(jù)類型及內(nèi)存占用:")
df_large.info(memory_usage='deep')

注釋:使用 astype() 方法轉(zhuǎn)換數(shù)據(jù)類型。在轉(zhuǎn)換前,最好先了解列中數(shù)據(jù)的實(shí)際范圍和特性。使用 df.info(memory_usage='deep') 可以查看更準(zhǔn)確的內(nèi)存占用情況(特別是對(duì)于 object 類型的列)。

總結(jié)

掌握 Pandas 的進(jìn)階技巧對(duì)于高效處理日益復(fù)雜的數(shù)據(jù)至關(guān)重要。通過運(yùn)用鏈?zhǔn)讲僮魈嵘a可讀性與流暢性,借助 pipe() 無縫集成自定義函數(shù),利用 explode()、stack()/unstack() 巧妙重塑數(shù)據(jù)結(jié)構(gòu),使用 assign() 優(yōu)雅創(chuàng)建新列,以及通過 eval()/query() 和合理選擇數(shù)據(jù)類型 來優(yōu)化性能,你將能夠更自信、更專業(yè)地駕馭 Pandas,將數(shù)據(jù)分析工作提升到新的高度。

不斷實(shí)踐這些技巧,并結(jié)合具體的數(shù)據(jù)問題進(jìn)行思考,你將發(fā)現(xiàn)Pandas的強(qiáng)大遠(yuǎn)不止于此。

責(zé)任編輯:趙寧寧 來源: Python數(shù)智工坊
相關(guān)推薦

2015-03-10 11:34:22

SQL Server數(shù)據(jù)匯總ROUPBY

2010-08-25 15:24:13

職業(yè)定位

2015-09-23 09:43:59

2015-09-24 10:06:59

2024-08-06 08:00:00

SQL Query數(shù)據(jù)庫

2020-06-03 07:00:12

云成本監(jiān)控工具云散亂

2011-09-29 15:30:57

云計(jì)算

2009-10-09 09:39:47

2025-03-11 14:09:04

2020-02-04 12:44:03

混合云架構(gòu)公共云

2009-01-03 09:14:00

網(wǎng)絡(luò)模塊選購(gòu)

2019-12-20 08:00:00

云賬號(hào)劫持網(wǎng)絡(luò)釣魚云安全

2025-03-18 10:25:59

2016-04-29 10:02:39

2023-08-02 08:47:55

聚合框架MongoDB

2010-01-20 11:09:18

虛擬服務(wù)器安全

2022-11-17 11:52:35

pandasPySpark大數(shù)據(jù)

2024-05-08 14:05:03

時(shí)間序列數(shù)據(jù)

2024-04-03 07:46:41

PythonReduce函數(shù)工具

2017-10-30 11:03:11

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)