DataFrames.jl

Mengelola data tabular dengan DataFrames.jl beserta alur kerja split-apply-combine untuk agregasi.

Daftar isi

DataFrames.jl adalah package Julia untuk data tabular berupa kolom bernama, bentuk yang lazim pada basis data dan tabel pengolah angka. Topik ini membahas pembentukan DataFrame dari kolom, akses dan pemilihan kolom, perubahan nama kolom, ringkasan statistik dengan describe, serta alur kerja split-apply-combine untuk agregasi. Data contohnya berupa nilai ujian enam siswa dari dua kelas, dan setiap keluaran berasal dari eksekusi nyata pada Julia 1.13.

Membangun DataFrame dari kolom#

Konstruktor DataFrame menerima nama kolom beserta vektornya sekaligus, ditulis dalam bentuk nama = vektor. Setiap kolom berupa satu Vector dengan tipe sendiri, sedangkan posisi pada vektor menentukan barisnya, sehingga seluruh kolom wajib memiliki panjang yang sama. Fungsi nrow dan ncol menghitung banyaknya baris dan kolom, sedangkan names menampilkan daftar nama kolomnya.

Akses kolom dilakukan melalui df.kolom atau df[!, :kolom]. Kedua bentuk itu menunjuk objek kolom yang sama, dan hal itu dibuktikan oleh operator === pada contoh berikut. Fungsi first(df, n) mengambil baris pertama sebanyak n sebagai DataFrame baru, sedangkan eachrow mengiterasi baris per baris.

julia Contoh: DataFrame dari kolom bernama
using DataFrames

nilai = DataFrame(
    nama = ["Ali", "Budi", "Citra", "Dewi", "Eko", "Fajar"],
    kelas = ["7A", "7A", "7A", "7B", "7B", "7B"],
    nilai = [78, 85, 91, 82, 74, 88],
)

println(nrow(nilai), " baris, ", ncol(nilai), " kolom")
println(names(nilai))
println(nilai.nilai)
println(nilai.kelas === nilai[!, :kelas])
for baris in eachrow(first(nilai, 2))
    println(baris.nama, " kelas ", baris.kelas, " nilai ", baris.nilai)
end
println(eltype(nilai.nilai))
text Keluaran
6 baris, 3 kolom
["nama", "kelas", "nilai"]
[78, 85, 91, 82, 74, 88]
true
Ali kelas 7A nilai 78
Budi kelas 7A nilai 85
Int64

Keluaran true pada baris keempat menunjukkan nilai.kelas dan nilai[!, :kelas] merujuk pada objek yang sama, sehingga keduanya dapat dipertukarkan. Fungsi eltype melaporkan tipe elemen kolom, dan kolom nilai bertipe Int64, sehingga perhitungan aritmetika pada kolom itu aman. Dua baris terakhir yang dicetak berasal dari first(nilai, 2), yaitu DataFrame enam baris yang dipangkas menjadi dua baris pertamanya.

Memilih dan mengganti nama kolom#

Fungsi select mengembalikan DataFrame baru yang hanya memuat kolom yang disebut, dengan urutan mengikuti argumennya. Fungsi rename mengganti nama kolom melalui pasangan :lama => :baru tanpa menyentuh isinya. Keduanya memiliki varian yang berakhir dengan tanda seru, yaitu select! dan rename!, yang mengubah objek aslinya secara langsung. Konvensi Julia menetapkan tanda seru sebagai penanda fungsi yang memutasi argumennya, sedangkan versi tanpa tanda seru selalu mengembalikan salinan.

julia Contoh: select dan rename
using DataFrames

nilai = DataFrame(
    nama = ["Ali", "Budi", "Citra", "Dewi", "Eko", "Fajar"],
    kelas = ["7A", "7A", "7A", "7B", "7B", "7B"],
    nilai = [78, 85, 91, 82, 74, 88],
)

skor = rename(select(nilai, :nama, :nilai), :nilai => :skor)
println(names(skor))
select!(skor, :nama)
println(names(skor))
text Keluaran
["nama", "skor"]
["nama"]

Pada contoh di atas, select(nilai, :nama, :nilai) menyalin dua kolom ke objek baru, rename mengubah kolom nilai menjadi skor pada salinan tersebut, dan select!(skor, :nama) memangkas skor di tempat. DataFrame nilai tidak terpengaruh oleh seluruh rangkaian itu karena setiap langkah bekerja pada salinan. Pemisahan ini mencegah perubahan yang tidak disengaja pada data asli selama eksplorasi berlangsung. Pola seleksi simbol tunggal yang sama juga dipakai oleh fungsi lain seperti combine dan subset, sehingga kebiasaan ini dapat digunakan ulang di seluruh package.

Ringkasan statistik dengan describe#

Fungsi describe menghasilkan DataFrame ringkasan yang barisnya adalah kolom data, lengkap dengan statistik seperti mean, min, median, dan max, ditambah nmissing untuk menghitung nilai hilang serta eltype untuk tipe kolomnya. Argumen kata kunci cols membatasi perhitungan pada kolom tertentu, sehingga ringkasan tetap fokus ketika tabelnya lebar. Keluarannya ditampilkan dengan show beserta argumen allrows dan allcols agar tidak ada baris atau kolom yang tampilannya dipangkas.

julia Contoh: describe pada satu kolom
using DataFrames

nilai = DataFrame(
    nama = ["Ali", "Budi", "Citra", "Dewi", "Eko", "Fajar"],
    kelas = ["7A", "7A", "7A", "7B", "7B", "7B"],
    nilai = [78, 85, 91, 82, 74, 88],
)

stat = describe(nilai; cols = :nilai)
show(stdout, stat; allrows = true, allcols = true)
text Keluaran
1×7 DataFrame
 Row │ variable  mean     min    median   max    nmissing  eltype
     │ Symbol    Float64  Int64  Float64  Int64  Int64     DataType
─────┼──────────────────────────────────────────────────────────────
   1 │ nilai        83.0     74     83.5     91         0  Int64

Ringkasan pada keluaran menunjukkan rata-rata 83.0, median 83.5, nilai terendah 74, dan nilai tertinggi 91, sesuai dengan enam nilai pada data. Nilai nmissing berupa nol karena tidak ada nilai hilang pada kolom tersebut. Apabila cols tidak diberikan, seluruh kolom dirangkum, dan kolom teks tetap dilaporkan dengan informasi seperti nilai terkecil serta terbesarnya. Angka-angka ini berguna sebagai pembanding cepat sebelum agregasi yang lebih rinci dijalankan.

Alur split-apply-combine#

Agregasi per kelompok pada DataFrames.jl mengikuti alur kerja split-apply-combine, yaitu memecah tabel per nilai kunci, menjalankan fungsi pada tiap pecahan, lalu menyatukan hasilnya menjadi tabel baru. Fungsi groupby menuntaskan tahap split dengan mengembalikan GroupedDataFrame, yaitu kumpulan sub-DataFrame yang dibagi berdasarkan kolom kuncinya. Fungsi combine menuntaskan dua tahap sisanya dengan menerapkan fungsi agregasi pada setiap kelompok, kemudian mengumpulkan hasilnya menjadi satu tabel. Diagram berikut merangkum alurnya.

DataFrame6 baris × 3 kolomgroupby2 kelompokGroupedDataFrameapplyper kelompokmean, lengthcombineringkasan2 × 5 kolomsplit: groupby memecah DataFrame menjadi kelompok per nilai kolom kelasapply: fungsi agregasi dijalankan pada setiap kelompokcombine: hasil semua kelompok disatukan menjadi satu tabel baru
Alur split-apply-combine dari DataFrame hingga tabel ringkasan
julia Contoh: groupby dan combine
using DataFrames, Statistics

nilai = DataFrame(
    nama = ["Ali", "Budi", "Citra", "Dewi", "Eko", "Fajar"],
    kelas = ["7A", "7A", "7A", "7B", "7B", "7B"],
    nilai = [78, 85, 91, 82, 74, 88],
)

gdf = groupby(nilai, :kelas)
println(length(gdf))
ringkasan = combine(
    gdf,
    :nilai => mean => :rata_rata,
    :nilai => minimum => :terendah,
    :nilai => maximum => :tertinggi,
    :nilai => length => :jumlah,
)
show(stdout, ringkasan; allrows = true, allcols = true)
text Keluaran
2
2×5 DataFrame
 Row │ kelas   rata_rata  terendah  tertinggi  jumlah
     │ String  Float64    Int64     Int64      Int64
─────┼────────────────────────────────────────────────
   1 │ 7A        84.6667        78         91       3
   2 │ 7B        81.3333        74         88       3

Panggilan groupby(nilai, :kelas) menghasilkan dua kelompok, sesuai dengan keluaran length(gdf) yang bernilai 2. Setiap pasangan seperti :nilai => mean => :rata_rata dibaca sebagai ambil kolom nilai, terapkan mean, lalu simpan hasilnya dengan nama rata_rata. Kolom kunci kelas ikut muncul pada hasil karena menjadi pembeda kelompoknya. Rata-rata kelas 7A dihitung dari 78, 85, dan 91 dengan hasil 84.6667, sedangkan kelas 7B menghasilkan 81.3333. Fungsi combine juga dapat dipanggil langsung pada DataFrame tanpa groupby apabila agregasinya menyasar seluruh tabel.

Contoh utuh: agregasi dari berkas CSV#

Data nilai ujian pada topik ini tersimpan sebagai berkas nilai_ujian.csv di samping artikel, dengan isinya sebagai berikut.

text nilai_ujian.csv
nama,kelas,nilai
Ali,7A,78
Budi,7A,85
Citra,7A,91
Dewi,7B,82
Eko,7B,74
Fajar,7B,88

Skrip agregasi.jl membaca berkas itu, mengelompokkan per kelas, lalu mencetak ringkasannya. Pemanggilan CSV.read diberi argumen kedua DataFrame sebagai tipe tujuan, dan argumen kata kunci stringtype = String membuat kolom teks bertipe String biasa. Format CSV beserta pilihannya dibahas pada topik Berkas dan Format.

julia agregasi.jl
# agregasi.jl
# Rata-rata nilai ujian per kelas dengan alur split-apply-combine.

using CSV, DataFrames, Statistics

nilai = CSV.read("nilai_ujian.csv", DataFrame; stringtype = String)

println("Data yang dibaca:")
show(stdout, nilai; allrows = true, allcols = true)

gdf = groupby(nilai, :kelas)
ringkasan = combine(
    gdf,
    :nilai => mean => :rata_rata,
    :nilai => minimum => :terendah,
    :nilai => maximum => :tertinggi,
    :nilai => length => :jumlah,
)

println()
println("Ringkasan per kelas:")
show(stdout, ringkasan; allrows = true, allcols = true)
text Keluaran
Data yang dibaca:
6×3 DataFrame
 Row │ nama    kelas   nilai
     │ String  String  Int64
─────┼───────────────────────
   1 │ Ali     7A         78
   2 │ Budi    7A         85
   3 │ Citra   7A         91
   4 │ Dewi    7B         82
   5 │ Eko     7B         74
   6 │ Fajar   7B         88
Ringkasan per kelas:
2×5 DataFrame
 Row │ kelas   rata_rata  terendah  tertinggi  jumlah
     │ String  Float64    Int64     Int64      Int64
─────┼────────────────────────────────────────────────
   1 │ 7A        84.6667        78         91       3
   2 │ 7B        81.3333        74         88       3

Keluarannya memperlihatkan data enam baris yang dibaca dari berkas, lalu tabel ringkasan dua baris hasil agregasinya. Skrip ini berdiri sendiri dan dapat dijalankan dari direktori topik dengan perintah julia agregasi.jl. Kolom jumlah berisi banyaknya siswa per kelas yang dihitung oleh fungsi length, sehingga setiap nilai pada kolom rata_rata dapat diperiksa ulang dari angka mentahnya. Argumen stringtype = String pada skrip menjaga tipe kolom teks tetap konsisten dengan DataFrame yang dibangun dari vektor string biasa pada bagian-bagian sebelumnya.

Kesalahan umum#

Tiga kesalahan berikut paling sering muncul saat DataFrames.jl dipraktikkan. Yang pertama dapat direproduksi pada contoh berikut.

julia Contoh: mengakses kolom yang tidak ada
using DataFrames

df = DataFrame(nama = ["Ali", "Budi"])

try
    println(df.alamat)
catch e
    println(typeof(e))
end
text Keluaran
ArgumentError
  • Mengakses kolom dengan nama yang tidak terdaftar, misalnya alamat pada contoh di atas. Eksekusinya berhenti dengan ArgumentError, sehingga daftar kolom sebaiknya diperiksa dengan names terlebih dahulu.
  • Mengira select, rename, dan combine mengubah data asli. Ketiganya mengembalikan salinan, sedangkan versi bertanda seru seperti select! memutasi objek aslinya.
  • Menampilkan tabel besar tanpa show dengan allrows dan allcols, lalu menyimpulkan datanya terpotong. REPL memangkas tampilan demi keterbacaan, sedangkan isinya tetap utuh.

Ringkasan#

  • DataFrame disusun dari kolom bernama yang masing-masing berupa Vector, dan df.kolom setara dengan df[!, :kolom].
  • select, rename, dan combine mengembalikan salinan, sedangkan varian bertanda seru mengubah objek aslinya.
  • describe merangkum statistik per kolom, dan argumen kata kunci cols membatasi kolom yang dihitung.
  • Alur split-apply-combine terdiri atas groupby untuk memecah data, fungsi agregasi pada combine untuk menerapkannya, dan tabel baru sebagai hasilnya.

Referensi#