在資料分析領域,有效地視覺化資料對於理解資料分佈和特徵至關重要。傳統的直方圖雖然常用,但其依賴分組策略的特性可能造成解讀偏差。本文將介紹更強大的核密度估計(KDE)圖,它不需要分組策略,並能更精確地展現資料分佈。此外,我們也將探討如何使用 Matplotlib 進行圖表客製化,包含使用 GridSpec 進行更靈活的佈局控制,以及如何使用散點圖和散佈矩陣來探索多變數之間的關係。最後,我們將示範如何使用 Pandas 和 Matplotlib 處理和視覺化真實世界的資料集,例如車輛燃油效率資料和包含分類別資料的資料集,以提供更全面的資料探索和分析方法。

資料視覺化進階技術:超越直方圖的限制

在資料分析中,視覺化是理解資料分佈和特徵的重要工具。雖然直方圖是常用的視覺化工具,但其對分組(binning)策略的依賴可能導致不同的解讀結果。幸運的是,我們可以使用一種更為強大的視覺化工具——核密度估計(Kernel Density Estimate, KDE)圖,它不需要選擇分組策略。

使用KDE圖進行資料視覺化

首先,需要安裝SciPy函式庫來支援KDE圖的繪製:

python -m pip install scipy

安裝完成後,可以透過將kind="kde"傳遞給pd.Series.plot來繪製KDE圖:

ser.plot(kind="kde")

對於pd.DataFrame,KDE圖可以清晰地展示出不同的分佈:

df.plot(kind="kde")

使用Matplotlib進行進一步的圖表自定義

雖然pandas提供了方便的視覺化功能,但有時需要使用Matplotlib進行更細緻的控制。首先,瞭解一些Matplotlib的基本概念是有幫助的。在Matplotlib中,figure指的是繪圖區域,而axessubplot則是該區域內用於繪圖的子區域。

如何使用Matplotlib自定義圖表

讓我們以一個包含書籍銷售資料的pd.Series為例,嘗試在同一個figure中以三種不同的方式繪製它:線圖、條形圖和餅圖。首先,我們呼叫plt.subplots(nrows=1, ncols=3)來設定繪圖區域,這將傳回一個包含figure和一系列Axes物件的二元組。

ser = pd.Series(
    (x ** 2 for x in range(7)),
    name="book_sales",
    index=(f"Day {x + 1}" for x in range(7)),
    dtype=pd.Int64Dtype(),
)

fig, axes = plt.subplots(nrows=1, ncols=3)
ser.plot(ax=axes[0])
ser.plot(kind="bar", ax=axes[1])
ser.plot(kind="pie", ax=axes[2])

圖表解密:

  1. plt.subplots(nrows=1, ncols=3)建立了一個包含1行3列的子圖陣列。
  2. ser.plot(ax=axes[0])在第一個子圖中繪製了線圖。
  3. ser.plot(kind="bar", ax=axes[1])在第二個子圖中繪製了條形圖。
  4. ser.plot(kind="pie", ax=axes[2])在第三個子圖中繪製了餅圖。

使用GridSpec進行更靈活的佈局控制

為了更好地控制每個子圖的大小和位置,可以使用Matplotlib的GridSpec。透過建立一個2x2的網格,可以將條形圖和線圖放在第一行,而餅圖則佔據整個第二行。

from matplotlib.gridspec import GridSpec

fig = plt.figure()
gs = GridSpec(2, 2, figure=fig)
ax0 = fig.add_subplot(gs[0, 0])
ax1 = fig.add_subplot(gs[0, 1])
ax2 = fig.add_subplot(gs[1, :])

ser.plot(ax=ax0)
ser.plot(kind="bar", ax=ax1)
ser.plot(kind="pie", ax=ax2)

圖表解密:

  1. GridSpec(2, 2, figure=fig)建立了一個2x2的網格佈局。
  2. fig.add_subplot(gs[0, 0])在網格的第一行第一列增加了一個子圖。
  3. fig.add_subplot(gs[0, 1])在網格的第一行第二列增加了一個子圖。
  4. fig.add_subplot(gs[1, :])在網格的第二行跨列增加了一個子圖。

探索散點圖

散點圖是一種非常強大的視覺化工具,可以用來展示兩個變數之間的關係、測量個別資料點的規模,甚至可以看到這些關係和規模如何在不同的類別中變化。

如何建立散點圖

散點圖至少需要兩個變數,因此只能使用pd.DataFrame來建立。首先,建立一個包含四個不同欄位的樣本pd.DataFrame,其中三個是連續變數,第四個是用於分類別資料點的顏色。

df = pd.DataFrame({
    "var_a": [1, 2, 3, 4, 5],
    "var_b": [1, 2, 4, 8, 16],
    "var_c": [500, 200, 600, 100, 400],
    "var_d": ["blue", "orange", "gray", "blue", "gray"],
})
df = df.convert_dtypes(dtype_backend="numpy_nullable")

圖表翻譯:

此DataFrame包含了四個變數:var_a、var_b、var_c和var_d。其中var_a、var_b和var_c是數值變數,而var_d是分類別變數,用於表示不同的類別。

資料視覺化:深入探索車輛燃油效率資料

在進行資料分析時,視覺化是一種強大的工具,可以幫助我們快速理解資料的分佈和關係。本篇文章將以美國能源部發布的車輛燃油經濟性資料為例,展示如何使用散佈圖和散佈矩陣來探索車輛的城市和高速公路燃油效率之間的關係。

散佈圖:城市與高速公路燃油效率的關係

首先,我們讀取資料集中的部分欄位,包括 city08(城市裡程每加侖)、highway08(高速公路里程每加侖)、VClass(車輛型別)、fuelCost08(年度燃油成本)和 year(車輛年份)。然後,我們篩選出 2015 年及以後的小客車資料。

import pandas as pd

# 讀取資料
df = pd.read_csv(
    "data/vehicles.csv.zip",
    dtype_backend="numpy_nullable",
    usecols=["city08", "highway08", "VClass", "fuelCost08", "year"],
)

# 篩選小客車資料
car_classes = (
    "Subcompact Cars",
    "Compact Cars",
    "Midsize Cars",
    "Large Cars",
    "Two Seaters",
)
mask = (df["year"] >= 2015) & df["VClass"].isin(car_classes)
df = df[mask]

接下來,我們使用散佈圖來展示城市和高速公路燃油效率之間的關係。

df.plot(
    kind="scatter",
    x="city08",
    y="highway08",
)

內容解密:

  • kind="scatter":指定繪製散佈圖。
  • x="city08"y="highway08":分別指定 X 軸和 Y 軸的資料欄位。

為散佈圖新增顏色和大小屬性

為了進一步分析資料,我們可以根據車輛型別為資料點著色,並根據年度燃油成本調整資料點的大小。

# 將 VClass 轉換為分類別資料型別
classes_ser = pd.Series(car_classes, dtype=pd.StringDtype())
cat = pd.CategoricalDtype(classes_ser)
df["VClass"] = df["VClass"].astype(cat)

# 繪製散佈圖
df.plot(
    kind="scatter",
    x="city08",
    y="highway08",
    c="VClass",
    colormap="Dark2",
    s="fuelCost08"/25,
    alpha=0.4,
)

內容解密:

  • c="VClass":根據車輛型別著色。
  • colormap="Dark2":指定顏色對映。
  • s="fuelCost08"/25:根據年度燃油成本調整資料點大小,並進行縮放以避免過大的點。
  • alpha=0.4:設定資料點的透明度。

散佈矩陣:探索多變數之間的關係

除了散佈圖外,散佈矩陣是另一種有用的視覺化工具,可以展示多個連續變數之間的成對關係。

from pandas.plotting import scatter_matrix

scatter_matrix(df)

內容解密:

  • scatter_matrix(df):生成散佈矩陣,展示 df 中所有連續欄位之間的關係。

透過這些視覺化技術,我們可以更深入地瞭解車輛燃油效率資料的特性,並發現變數之間的潛在關係。這些見解可以為進一步的分析和決策提供有價值的參考。

資料視覺化與分類別資料探索

在前面的章節中,我們已經使用散佈圖來視覺化資料之間的關係。在本章節中,我們將進一步探討如何使用視覺化技術來快速識別分類別資料的分佈。

探索分類別資料

分類別資料是指用於分類別和導航資料的欄位,但其值在聚合時通常沒有太大的意義。例如,如果我們正在處理一個包含「眼睛顏色」欄位的資料集,其值可能為 Brown、Green、Hazel、Blue 等,我們可以使用此欄位來導航資料集並回答諸如「對於眼睛顏色為 X 的資料列,平均瞳孔直徑是多少?」之類別的問題。但是,我們不會問「眼睛顏色的總和是多少?」這樣沒有意義的問題。

相反,連續資料是指通常用於聚合的資料。例如,對於「瞳孔直徑」欄位,我們可以計算其平均值、最小值、最大值、標準差等。在某些情況下,資料可能是分類別或連續的,取決於我們如何使用它。

如何進行分類別資料視覺化

首先,我們需要載入美國能源部提供的車輛資料集。這個資料集包含了分類別和連續資料的混合,因此是一個很好的例子:

df = pd.read_csv(
    "data/vehicles.csv.zip",
    dtype_backend="numpy_nullable",
)
df.head()

在載入資料時,我們可能會收到一個警告,指出某些欄位具有混合型別。讓我們來看看這些欄位:

df.iloc[:, [72, 74, 75, 77]]

這些欄位的名稱為 rangeA、mfrCode、c240Dscr 和 c240bDscr。讓我們進一步檢查 rangeA 欄位:

df["rangeA"].value_counts()

內容解密:

  • df["rangeA"].value_counts() 用於計算 rangeA 欄位中每個值的出現次數。
  • 從結果中可以看到,rangeA 欄位包含許多不同的值,包括一些非數值的值,如 “240/290/290” 和 “230/350”。

由於 rangeA 欄位包含非數值的值,我們需要將其視為字串欄位。讓我們使用 pd.Series.str.isnumeric()pd.Series.idxmax() 來找出第一個非整數值的資料列:

df["rangeA"].str.isnumeric().idxmax()

內容解密:

  • df["rangeA"].str.isnumeric() 用於檢查 rangeA 欄位中的每個值是否為數值。
  • idxmax() 用於找出第一個非數值值的索引。

為了避免 pandas 在讀取 CSV 檔案時發出警告,我們可以明確指定這些欄位的型別為字串:

df = pd.read_csv(
    "data/vehicles.csv.zip",
    dtype_backend="numpy_nullable",
    dtype={
        "rangeA": pd.StringDtype(),
        "mfrCode": pd.StringDtype(),
        "c240Dscr": pd.StringDtype(),
        "c240bDscr": pd.StringDtype(),
    },
)

內容解密:

  • 在讀取 CSV 檔案時,我們使用 dtype 引數來指定某些欄位的型別為字串,以避免 pandas 發出警告。

探索連續型資料

在前面的章節中,我們已經探討了分類別資料的處理方法。本章節將重點轉向連續型資料的探索。我們將沿用相同的vehicles資料集,因為它同時包含了分類別和連續型資料,這使得它非常適合用於展示如何處理這兩種型別的資料。

步驟一:載入資料集

首先,我們需要載入vehicles資料集。載入資料時,我們指定了資料型別,以確保某些欄位被正確地讀取為字串型別。

df = pd.read_csv(
    "data/vehicles.csv.zip",
    dtype_backend="numpy_nullable",
    dtype={
        "rangeA": pd.StringDtype(),
        "mfrCode": pd.StringDtype(),
        "c240Dscr": pd.StringDtype(),
        "c240bDscr": pd.StringDtype()
    }
)
df.head()

輸出結果:

   barrels08  bar-relsA08  charge120  ...  phevCity  phevHwy  phevComb
0  14.167143          0.0        0.0  ...         0         0         0
1  27.046364          0.0        0.0  ...         0         0         0
2  11.018889          0.0        0.0  ...         0         0         0
3  27.046364          0.0        0.0  ...         0         0         0
4  15.658421          0.0        0.0  ...         0         0         0
5 rows × 84 columns

#### 程式碼解析:

  • 使用 pd.read_csv 載入壓縮的 CSV 資料集。
  • 指定 dtype_backend="numpy_nullable" 以最佳化資料儲存。
  • 使用 dtype 引數指定某些欄位應被讀取為字串型別。

步驟二:選擇連續型資料欄位

在前面的章節中,我們使用了 pd.DataFrame.select_dtypes 方法並傳入 include= 引數來篩選出分類別資料欄位。這裡,我們將使用相同的函式,但改為傳入 exclude= 引數,以此來選出連續型資料欄位。

df.select_dtypes(exclude=["string"]).columns

輸出結果:

Index(['barrels08', 'barrelsA08', 'charge120', 'charge240', 'city08',
       'city08U', 'cityA08', 'cityA08U', 'cityCD', 'cityE', 'cityUF', 'co2',
       'co2A', 'co2TailpipeAGpm', 'co2TailpipeGpm', 'comb08', 'comb08U',
       'combA08', 'combA08U', 'combE', 'combinedCD', 'combinedUF', 'cylinders',
       ...],
      dtype='object')

#### 程式碼解析:

  • 使用 exclude=["string"] 引數來排除字串型別的欄位,從而選出連續型資料欄位。

圖表視覺化

為了更直觀地理解這些連續型資料的分佈情況,我們可以使用直方圖或密度圖來進行視覺化。這裡我們將使用 matplotlibseaborn 這兩個函式庫來實作。

import seaborn as sns
import matplotlib.pyplot as plt

# 設定要視覺化的欄位
continuous_columns = df.select_dtypes(exclude=["string"]).columns[:9]

# 建立一個3x3的子圖網格
fig, axes = plt.subplots(nrows=3, ncols=3, figsize=(15, 10))

# 對每個欄位繪製直方圖
for index, column in enumerate(continuous_columns):
    row = index // 3
    col = index % 3
    ax = axes[row, col]
    sns.histplot(df[column], ax=ax, kde=True)
    ax.set_title(column)

plt.tight_layout()
plt.show()

#### 圖表翻譯:

此圖表展示了所選連續型資料欄位的直方圖和核密度估計(KDE)。透過觀察這些圖表,我們可以瞭解每個欄位的資料分佈情況,包括其集中趨勢、離散程度以及是否存在多峰分佈等特性。

#### 程式碼解析:

  • 使用 seabornhistplot 函式來繪製直方圖,並啟用 KDE 以顯示資料的密度分佈。
  • 使用 matplotlib.pyplot.subplots 建立一個3x3的子圖網格,以便同時展示多個欄位的分佈情況。
  • 對每個子圖設定標題,以標示所對應的資料欄位。

透過上述步驟,我們能夠有效地探索和分析連續型資料,為後續的資料分析和模型建立奠定基礎。