CSV unggul karena sederhana, tetapi kesederhanaan itu juga membatasi: CSV tidak mengenal banyak lembar kerja, tidak menyimpan format, dan tidak dipakai para penyusun laporan untuk bekerja sehari-hari. Di banyak organisasi, laporan justru hidup di Excel — laporan mingguan manajer Toko Kopi Kita, rekap kantor pusat, hingga data harga dari dinas tiba sebagai berkas .xlsx. Pada chapter ini kita membaca workbook Excel dengan pd.read_excel(): memilih sheet, melewati baris judul, memilih kolom, menangani nilai kosong, lalu menggabungkan dua sheet menjadi satu tabel analisis.

Workbook dan Engine-nya #

Berkas .xlsx adalah satu paket berisi lembar kerja (sheet), dan tiap sheet adalah kisi sel yang bisa memuat tabel, judul, sampai catatan pinggir. Berkas latihan chapter ini, laporan_penjualan.xlsx, memuat dua sheet dengan susunan berikut:

SheetSusunan barisIsi
PenjualanBaris 1 judul laporan, baris 2 nama kolom, 8 baris dataPenjualan 20-23 Januari 2026
ProdukBaris 1 nama kolom, 4 baris dataDaftar produk, pemasok, dan harga

Satu hal teknis sebelum masuk ke kode: pandas tidak membaca .xlsx sendiri. Pekerjaan itu diserahkan kepada library openpyxl yang berperan sebagai engine pembaca — di lingkungan latihan library tersebut sudah terpasang, dan pada lingkungan lain cukup dipasang lewat pip install openpyxl. Penjelasan lengkap tentang membaca-menulis Excel pada pandas tersedia di pandas user guide .

Membaca Default dan Masalah Baris Judul #

Laporan buatan manusia hampir selalu memulai tabel dengan judul besar di atasnya. Kita baca dulu laporan_penjualan.xlsx apa adanya untuk melihat akibatnya:

python
import pandas as pd

df = pd.read_excel("laporan_penjualan.xlsx")
print(df.head(3))
print()
print("Nama kolom:", list(df.columns))

Output yang Dihasilkan:

text
  Laporan Penjualan Toko Kopi Kita    Unnamed: 1 Unnamed: 2 Unnamed: 3
0                          tanggal        produk     jumlah  pelanggan
1                       2026-01-20     kopi susu         12       Budi
2                       2026-01-20  matcha latte          8       Siti

Nama kolom: ['Laporan Penjualan Toko Kopi Kita', 'Unnamed: 1', 'Unnamed: 2', 'Unnamed: 3']

Mari kita telaah baris-baris pentingnya:

  1. Baris 3 (pd.read_excel(...) tanpa parameter): pandas membuka sheet pertama secara bawaan dan menjadikan baris pertamanya — baris judul — sebagai nama kolom.
  2. Baris 6 (list(df.columns)): nama kolom yang diperoleh bertepatan dengan judul laporan di kolom pertama, sementara tiga kolom lainnya bernama Unnamed: 1 sampai Unnamed: 3. Nama kolom yang sesungguhnya (tanggal, produk, jumlah, pelanggan) tergelincir menjadi baris data pertama — terlihat jelas pada hasil df.head(3).

Pembacaan seperti ini tidak mengeluarkan galat, tetapi seluruh analisis lanjutan akan salah alamat kolomnya. Solusinya satu parameter: skiprows.

Melewati Baris Judul dengan skiprows #

skiprows=1 memerintahkan pandas melewati satu baris paling atas sebelum membaca, sehingga baris kedua — baris nama kolom — menempati posisi header. Sekaligus kita tambahkan dua penyesuaian yang sudah akrab dari chapter CSV: sheet_name untuk memilih sheet secara eksplisit dan na_values untuk penanda kosong buatan.

python
import pandas as pd

penjualan = pd.read_excel(
    "laporan_penjualan.xlsx",
    sheet_name="Penjualan",
    skiprows=1,
    na_values=["-"],
)
print(penjualan)
print()
print(penjualan.dtypes)
print()
print("Nilai hilang per kolom:")
print(penjualan.isna().sum())

Output yang Dihasilkan:

text
      tanggal        produk  jumlah pelanggan
0  2026-01-20     kopi susu      12      Budi
1  2026-01-20  matcha latte       8      Siti
2  2026-01-21     americano       5      Budi
3  2026-01-21     kopi susu      15      Dewi
4  2026-01-22     teh tawar       6       NaN
5  2026-01-22     kopi susu       9      Agus
6  2026-01-23  matcha latte       7       NaN
7  2026-01-23     americano       4      Rani

tanggal      object
produk       object
jumlah        int64
pelanggan    object
dtype: object

Nilai hilang per kolom:
tanggal      0
produk       0
jumlah       0
pelanggan    2
dtype: int64

Mari kita telaah baris-baris pentingnya:

  1. Baris 5 (sheet_name="Penjualan"): tanpa parameter ini, pandas memakai sheet pertama. Menuliskan nama sheet membuat pilihan eksplisit dan tetap benar meski urutan sheet diubah pembuat laporan.
  2. Baris 6 (skiprows=1): baris judul dilewati; tanggal, produk, jumlah, dan pelanggan kini menjadi nama kolom yang benar.
  3. Baris 7 (na_values=["-"]): tanda - pada baris matcha latte tanggal 23 Januari diubah menjadi NaN. Sel yang memang kosong — pelanggan teh tawar tanggal 22 Januari — sudah menjadi NaN secara bawaan; dua asal nilai hilang yang berbeda berakhir pada penanda yang sama.
  4. Baris 14 (isna().sum()): ringkasan menegaskan dua nilai hilang seluruhnya berada di kolom pelanggan — pola yang wajar untuk laporan kasir yang tidak selalu mencatat nama pembeli.

Membaca Sheet Lain dan Memilih Kolom #

Sheet kedua, Produk, menyimpan harga satuan — dan justru di situlah nilainya bagi analisis: omzet tidak dapat dihitung dari sheet Penjualan saja. Kita baca kedua sheet dengan usecols agar hanya kolom yang diperlukan yang dimuat, lalu gabungkan keduanya dengan merge() — method yang didokumentasikan lengkap di pandas.DataFrame.merge .

python
import pandas as pd

penjualan = pd.read_excel(
    "laporan_penjualan.xlsx",
    sheet_name="Penjualan",
    skiprows=1,
    na_values=["-"],
    usecols=["tanggal", "produk", "jumlah"],
)
produk = pd.read_excel(
    "laporan_penjualan.xlsx",
    sheet_name="Produk",
    usecols=["produk", "harga"],
)

tabel = penjualan.merge(produk, on="produk", how="left")
tabel["omzet"] = tabel["jumlah"] * tabel["harga"]
print(tabel)
print()
print("Total omzet empat hari:", tabel["omzet"].sum())

Output yang Dihasilkan:

text
      tanggal        produk  jumlah  harga   omzet
0  2026-01-20     kopi susu      12  24000  288000
1  2026-01-20  matcha latte       8  28000  224000
2  2026-01-21     americano       5  22000  110000
3  2026-01-21     kopi susu      15  24000  360000
4  2026-01-22     teh tawar       6  20000  120000
5  2026-01-22     kopi susu       9  24000  216000
6  2026-01-23  matcha latte       7  28000  196000
7  2026-01-23     americano       4  22000   88000

Total omzet empat hari: 1602000

Mari kita telaah baris-baris pentingnya:

  1. Baris 3-9 (penjualan): pembacaan sheet Penjualan kini memuat hanya tiga kolom — pelanggan yang bolong-bolong tidak diperlukan untuk menghitung omzet, jadi ditinggalkan sejak awal.
  2. Baris 10-14 (produk): sheet Produk dibaca dengan usecols=["produk", "harga"]; kolom pemasok yang tidak terpakai tidak ikut dimuat.
  3. Baris 16 (merge): kedua tabel digabung berdasarkan nilai yang sama pada kolom produk. how="left" mempertahankan seluruh baris penjualan lalu menempelkan harga yang cocok di baris masing-masing.
  4. Baris 17 (omzet): kolom baru dihitung dari jumlah dikali harga; penjumlahannya menghasilkan 1.602.000 rupiah untuk empat hari berjualan.

Keunikan Excel yang Perlu Dikenali #

Dua sifat Excel membedakannya dari CSV dan menuntut kewaspadaan ekstra. Pertama, tipe tersimpan longgar: Excel tidak mengenal tipe kolom — tipe melekat pada sel satu per satu, sehingga satu kolom boleh memuat angka di sel atas dan teks di sel bawah tanpa teguran. Tebakan tipe oleh pandas dapat berbeda dari niat penyusunnya; kolom yang tampak penuh angka bisa terbaca object hanya karena satu sel tersimpan sebagai teks. Kebiasaan memeriksa dtypes karena itu semakin penting.

Kedua, sel gabungan (merged cells): beberapa sel dilebur menjadi satu demi tampilan — judul besar atau kategori berjajar. Bagi pembaca data, peleburan itu hanya menyisakan nilai pada sel kiri-atas; sel sisanya kosong dan muncul sebagai NaN, lengkap dengan kehilangan konteks nilainya. Untuk berkas yang akan dibaca program, simpan setiap data pada sel tunggal dan letakkan judul di luar tabel — persis pola yang kita tangani dengan skiprows di atas.

Rangkuman #

  • pd.read_excel() membaca .xlsx melalui engine openpyxl; tanpa parameter, sheet pertama yang dipakai dan baris pertamanya menjadi nama kolom.
  • Baris judul membuat nama kolom bergeser menjadi Unnamed; skiprows=1 melewatinya sehingga baris nama kolom menempati posisi header.
  • sheet_name memilih sheet tertentu (atau None untuk semuanya), usecols memuat kolom tertentu saja, dan na_values menangani penanda kosong buatan selain sel kosong bawaan.
  • merge() menyatukan dua sheet menjadi satu tabel analisis — penjualan dan harga bertemu menjadi kolom omzet.
  • Excel menyimpan tipe per sel dan mengenal sel gabungan; keduanya menuntut pemeriksaan dtypes serta desain berkas yang memisahkan tampilan dari data.

Baik CSV maupun Excel berbagi satu wujud: tabel baris-kolom. Sistem modern seperti API dan aplikasi web justru bertukar data dalam bentuk lain — pasangan kunci-nilai yang saling bercabang, dengan nama JSON. Pada chapter berikutnya, Membaca File JSON , kita membaca struktur bertingkat itu dengan modul json bawaan Python dan pandas.