Berkas dan Format

Membaca dan menulis data dalam format CSV dan Arrow, beserta putaran serialisasi bolak-balik.

Daftar isi

Data yang diproses DataFrames.jl hampir selalu berpindah antara memori dan berkas. Topik ini membahas pembacaan serta penulisan berkas CSV dengan CSV.jl, putaran tulis dan baca format biner Arrow dengan Arrow.jl, dan arsip objek Julia dengan Serialization dari pustaka standar. Data contohnya kembali berupa nilai ujian enam siswa, sehingga isi setiap berkas mudah diperiksa ulang.

Berkas contoh dan pembacaan CSV#

Berkas contoh nilai_ujian.csv tersimpan 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

Fungsi CSV.read menerima jalur berkas dan tipe tujuannya. Argumen kedua DataFrame membuat hasilnya langsung berupa DataFrame, baris pertama berkas otomatis menjadi nama kolom, dan tipe setiap kolom dideteksi dari isinya. Argumen kata kunci stringtype = String membuat kolom teks bertipe String biasa. Tanpa argumen itu, CSV.jl memakai tipe InlineStrings yang dirancang untuk teks pendek.

julia Contoh: membaca berkas CSV
using CSV, DataFrames

nilai = CSV.read("nilai_ujian.csv", DataFrame; stringtype = String)
println(nrow(nilai), " baris")
println(nilai.nama)
println(eltype(nilai.nilai))
text Keluaran
6 baris
["Ali", "Budi", "Citra", "Dewi", "Eko", "Fajar"]
Int64

Keluaran menunjukkan enam baris terbaca, kolom nama berupa vektor string enam elemen, dan kolom nilai bertipe Int64. Tipe hasil deteksi ini layak diperiksa dengan eltype sebelum data diproses lebih jauh, karena tipe yang keliru biasanya baru tampak saat operasi berikutnya berjalan. Baris dan kolom yang terbaca selanjutnya dapat diperiksa dengan first(nilai, 3) maupun describe(nilai) dari DataFrames.jl, karena hasil CSV.read adalah DataFrame biasa yang mendukung seluruh operasi DataFrames.jl.

Fungsi CSV.write menuliskan DataFrame ke jalur yang diberikan, lengkap dengan baris header otomatis. Pemisah kolomnya dideteksi otomatis saat pembacaan dan dapat dipaksa melalui argumen delim, baik pada CSV.write maupun CSV.read. Pemisah titik koma lazim dipakai pada berkas yang berasal dari lokal (locale) yang menggunakan koma sebagai tanda desimal. Contoh berikut menulis tabel persediaan ke direktori sementara, lalu menampilkan isi teks berkasnya sebelum membaca ulang.

julia Contoh: menulis dan membaca ulang CSV
using CSV, DataFrames

tabel = DataFrame(
    barang = ["pensil", "buku", "penghapus"],
    jumlah = [24, 10, 7],
)

dir = mktempdir()
jalur = joinpath(dir, "persediaan.csv")
CSV.write(jalur, tabel)
bacaan = CSV.read(jalur, DataFrame; stringtype = String)
println(read(jalur, String))
show(stdout, bacaan; allrows = true, allcols = true)
text Keluaran
barang,jumlah
pensil,24
buku,10
penghapus,7

3×2 DataFrame
 Row │ barang     jumlah
     │ String     Int64
─────┼───────────────────
   1 │ pensil         24
   2 │ buku           10
   3 │ penghapus       7

Isi teks berkas menunjukkan header barang,jumlah diikuti tiga baris data, dan hasil bacaannya kembali berupa DataFrame tiga baris. Fungsi read(jalur, String) membaca seluruh isi berkas sebagai satu string, sehingga format hasil tulisan dapat diperiksa secara langsung sebelum dipakai.

Berkas dengan pemisah lain ditangani dengan argumen yang sama. Pada contoh berikut, delim = ';' dinyatakan eksplisit di sisi penulisan maupun pembacaan. CSV.jl juga mendeteksi pemisah secara otomatis dari isi berkas, tetapi penegasan eksplisit membuat maksud skrip terbaca dan tidak terpengaruh oleh isi berkas yang tidak lazim.

julia Contoh: delimiter titik koma
using CSV, DataFrames

dir = mktempdir()
jalur = joinpath(dir, "data-titik-koma.csv")
CSV.write(jalur, DataFrame(nama = ["Ali", "Budi"], nilai = [78, 85]); delim = ';')
println(read(jalur, String))

bacaan = CSV.read(jalur, DataFrame; delim = ';', stringtype = String)
println(bacaan.nama)
text Keluaran
nama;nilai
Ali;78
Budi;85

["Ali", "Budi"]

Berkas ditulis dengan pemisah titik koma, lalu kolom nama terbaca utuh sebagai vektor string. Pemisah yang dinyatakan pada kedua sisi memastikan penulis dan pembaca berkas memakai interpretasi yang sama.

Format Arrow#

Arrow adalah format biner berkolom yang menyimpan skema bersama datanya, sehingga tipe setiap kolom terjaga dari penulisan hingga pembacaan. Fungsi Arrow.write menuliskan tabel ke berkas .arrow, sedangkan Arrow.Table membacanya kembali. Ukuran berkasnya lebih kecil dan pembacaannya lebih cepat daripada teks CSV pada data besar, dan formatnya dibaca oleh banyak bahasa pemrograman sehingga cocok untuk pertukaran data. Kompresi tambahan tersedia melalui argumen compress berisi :lz4 atau :zstd.

julia Contoh: putaran Arrow.write dan Arrow.Table
using Arrow, DataFrames

tabel = DataFrame(
    nama = ["Ali", "Budi", "Citra"],
    kelas = ["7A", "7A", "7B"],
    nilai = [78, 85, 91],
)

dir = mktempdir()
jalur = joinpath(dir, "nilai.arrow")
Arrow.write(jalur, tabel)

hasil = Arrow.Table(jalur)
println(eltype(hasil.nama))
println(eltype(hasil.kelas))
println(eltype(hasil.nilai))
println(hasil.nilai)
text Keluaran
String
String
Int64
[78, 85, 91]

Tiga keluaran eltype menunjukkan String, String, dan Int64, persis seperti tipe kolom aslinya. Putaran ini menegaskan ciri utama Arrow, yaitu skema ikut tersimpan pada berkasnya, sehingga tipe kolom tidak perlu ditebak ulang saat membaca. Isi berkas .arrow tidak dapat dibaca langsung oleh editor teks karena berupa biner, sehingga pemeriksaan isinya dilakukan melalui Arrow.Table dan pencetakan kolomnya, sebagaimana nilai [78, 85, 91] pada keluaran.

Arsip objek dengan Serialization#

Pustaka standar Serialization menyimpan objek Julia apa pun ke berkas biner, termasuk Dict, struct, maupun DataFrame, melalui fungsi serialize dan deserialize. Putarannya memakai blok open dengan do, yaitu berkas dibuka untuk ditulis, diserialisasi, lalu ditutup otomatis setelah blok selesai. Formatnya khusus Julia dan bukan format pertukaran antarbahasa.

julia Contoh: putaran serialize dan deserialize
using DataFrames, Serialization

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

dir = mktempdir()
jalur = joinpath(dir, "nilai.jls")
open(jalur, "w") do io
    serialize(io, nilai)
end

kembali = open(jalur) do io
    deserialize(io)
end
println(typeof(kembali))
println(nrow(kembali), " baris, jumlah nilai = ", sum(kembali.nilai))
text Keluaran
DataFrame
6 baris, jumlah nilai = 498

Keluarannya menunjukkan objek yang kembali persis berupa DataFrame dengan enam baris, dan sum(kembali.nilai) menghitung 498 dari seluruh nilai. Representasi yang ditulis adalah internal Julia, sehingga struktur bersarang seperti vektor di dalam Dict pun utuh saat dibaca kembali. Arsip semacam ini cocok untuk hasil perhitungan menengah yang hanya dibaca kembali oleh Julia.

Contoh utuh: satu berkas, tiga format#

Skrip format-berkas.jl merangkai ketiga format pada satu putaran data, yaitu CSV sebagai pintu masuk, Arrow sebagai penyimpanan berkolom, dan Serialization sebagai arsip objek. Ketiganya menulis ke direktori sementara sehingga tidak meninggalkan sisa pada direktori proyek.

julia format-berkas.jl
# format-berkas.jl
# Putaran data: CSV masuk, Arrow disimpan dan dibaca ulang, DataFrame diarsipkan dengan Serialization.

using Arrow, CSV, DataFrames, Serialization

nilai = CSV.read("nilai_ujian.csv", DataFrame; stringtype = String)
println("CSV   : ", nrow(nilai), " baris, ", ncol(nilai), " kolom")

dir = mktempdir()

# tulis dan baca ulang format Arrow
jalur_arrow = joinpath(dir, "nilai.arrow")
Arrow.write(jalur_arrow, nilai)
hasil = Arrow.Table(jalur_arrow)
println("Arrow : ", nrow(hasil), " baris, eltype nilai = ", eltype(hasil.nilai))

# arsipkan objek DataFrame dengan Serialization dari pustaka standar
jalur_arsip = joinpath(dir, "nilai.jls")
open(jalur_arsip, "w") do io
    serialize(io, nilai)
end
kembali = open(jalur_arsip) do io
    deserialize(io)
end
println("Arsip : ", nrow(kembali), " baris, jumlah nilai = ", sum(kembali.nilai))
text Keluaran
CSV   : 6 baris, 3 kolom
Arrow : 6 baris, eltype nilai = Int64
Arsip : 6 baris, jumlah nilai = 498

Setiap baris keluaran menyatakan satu format beserta bukti keberhasilannya, mulai dari jumlah baris dan kolom yang terbaca dari CSV, tipe kolom yang terjaga pada Arrow, hingga jumlah nilai pada objek hasil deserialize. Urutan baris keluaran mengikuti urutan putarannya, yaitu CSV lebih dahulu, lalu Arrow, dan terakhir arsip. Seluruh berkas antara ditulis di dalam satu direktori mktempdir, sehingga pembersihannya cukup menghapus direktori itu. Pola tiga format ini lazim pada proyek data, yaitu CSV dari sumber luar, Arrow untuk kerja cepat, dan arsip biner untuk hasil antara.

Kesalahan umum#

Empat kesalahan berikut paling sering muncul saat format berkas dipraktikkan. Yang pertama dapat direproduksi pada contoh berikut.

julia Contoh: membaca berkas yang tidak ada
using CSV, DataFrames

try
    CSV.read("berkas-tidak-ada.csv", DataFrame)
catch e
    println(typeof(e))
end
text Keluaran
ArgumentError
  • Membaca berkas yang tidak ada. CSV.read berhenti dengan ArgumentError sebagaimana contoh di atas, sehingga keberadaan berkas diuji dengan isfile sebelum dibaca.
  • Memanggil CSV.read(jalur) tanpa argumen kedua. Hasilnya berupa CSV.File yang belum merupakan DataFrame, sedangkan bentuk CSV.read(jalur, DataFrame) langsung mengembalikan DataFrame.
  • Menukar pembaca antarformat. Berkas .arrow dibaca dengan Arrow.Table, sedangkan berkas teks CSV dibaca dengan CSV.read, karena pembaca yang keliru tidak memahami isinya.
  • Mengandalkan deteksi pemisah pada berkas yang teksnya memuat koma. Deteksi otomatis dapat salah memilih, sehingga argumen delim yang eksplisit lebih aman.

Ringkasan#

  • CSV.read(jalur, DataFrame) membaca teks CSV dengan header otomatis, dan CSV.write menuliskannya kembali dengan argumen delim yang dapat dipaksa.
  • Arrow menyimpan data berkolom biner dengan skema tertanam, sehingga eltype kolom hasil bacaan sama dengan aslinya.
  • Serialization menyimpan objek Julia apa pun, tetapi berkasnya hanya untuk Julia dan tidak dijamin terbaca antarversi.
  • mktempdir menampung berkas sementara, sehingga seluruh contoh tidak meninggalkan sisa pada direktori proyek.

Referensi#