DataFrame

Baris dan kolom, bentuk API, dan pencetakan schema.

Daftar isi

DataFrame adalah struktur data utama Spark SQL, yaitu kumpulan data terdistribusi yang tertata dalam baris dan kolom bertipe. Topik ini membahas anatomi DataFrame, cara membuatnya dari data literal, dan cara mengamatinya melalui show, printSchema, serta statistik ringkas. Setelah membacanya, isi dan struktur sebuah DataFrame dapat diperiksa dengan benar, mulai dari baris tunggal sampai daftar kolom beserta tipenya.

Baris dan kolom pada DataFrame#

DataFrame menyerupai tabel pada basis data: setiap baris mewakili satu catatan dan setiap kolom mewakili satu bidang dengan nama serta tipe data tertentu. Perbedaannya dengan tabel biasa terletak pada sebarannya. Baris-baris DataFrame terbagi ke dalam partisi yang bisa diproses secara paralel, sehingga jumlah barisnya tidak terbatas pada memori satu mesin.

Kolom pada DataFrame bertipe, misalnya long untuk bilangan bulat dan string untuk teks. Daftar nama kolom beserta tipenya membentuk schema, yaitu cetak biru struktur data yang mengikat seluruh baris. Ketika sebagian baris dibawa ke program driver melalui metode seperti first() atau collect(), setiap baris tiba sebagai objek Row yang menyimpan pasangan nama dan nilai sesuai schema, bukan sebagai tuple Python biasa.

API DataFrame bersifat deklaratif. Program menyatakan transformasi apa yang diinginkan, misalnya memilih kolom atau menyaring baris, dan Spark menyusun langkah eksekusinya sendiri. Transformasi seperti select atau orderBy tidak mengubah DataFrame asal, tetapi mengembalikan DataFrame baru, karena DataFrame bersifat immutable, yaitu tidak dapat diubah setelah dibuat. Eksekusinya juga bersifat lazy, artinya transformasi hanya benar-benar dijalankan ketika ada action seperti show() atau count() yang menuntut hasil.

Membuat DataFrame dari data literal#

Cara paling langsung untuk membuat DataFrame adalah spark.createDataFrame() dengan daftar tuple dan schema berbentuk string DDL. Data contoh di bawah berisi empat siswa dengan kolom id, nama, kelas, dan nilai.

python
# tidak-dijalankan
data = [
    (1, "Ali", "10A", 85),
    (2, "Budi", "10A", 90),
    (3, "Citra", "10B", 78),
    (4, "Dewi", "10B", 92),
]
df = spark.createDataFrame(data, "id long, nama string, kelas string, nilai long")

Argumen kedua berupa string DDL menetapkan nama dan tipe setiap kolom secara eksplisit, sehingga bilangan 1 pada data literal tercatat sebagai long, bukan tipe Python. Tanpa argumen tersebut, Spark tetap bisa menyimpulkan schema dari isi data, tetapi penulisan eksplisit membuat skrip lebih mudah dibaca dan bebas dari tebakan. DataFrame hasilnya siap dipakai untuk demonstrasi operasi-operasi dasar pada dua skrip berikutnya.

Mengamati struktur: columns, dtypes, printSchema#

Skrip pertama membuat DataFrame siswa lalu menampilkannya beserta strukturnya. Skrip ini menambahkan .config("spark.sql.shuffle.partitions", "2") karena orderBy memicu shuffle, dan nilai default 200 partisi jelas berlebihan untuk empat baris data.

python pengamal_dataframe.py
from pyspark.sql import SparkSession

spark = (
    SparkSession.builder
    .master("local[2]")
    .appName("pengamal-dataframe")
    .config("spark.sql.shuffle.partitions", "2")
    .getOrCreate()
)

data = [
    (1, "Ali", "10A", 85),
    (2, "Budi", "10A", 90),
    (3, "Citra", "10B", 78),
    (4, "Dewi", "10B", 92),
]
df = spark.createDataFrame(data, "id long, nama string, kelas string, nilai long")

print("columns:", df.columns)
print("dtypes:", df.dtypes)

print("=== printSchema ===")
df.printSchema()

print("=== show ===")
df.orderBy("id").show()

print("jumlah baris:", df.count())

spark.stop()
text Keluaran
WARNING: Using incubator modules: jdk.incubator.vector
columns: ['id', 'nama', 'kelas', 'nilai']
dtypes: [('id', 'bigint'), ('nama', 'string'), ('kelas', 'string'), ('nilai', 'bigint')]
=== printSchema ===
root
 |-- id: long (nullable = true)
 |-- nama: string (nullable = true)
 |-- kelas: string (nullable = true)
 |-- nilai: long (nullable = true)

=== show ===
+---+-----+-----+-----+
| id| nama|kelas|nilai|
+---+-----+-----+-----+
|  1|  Ali|  10A|   85|
|  2| Budi|  10A|   90|
|  3|Citra|  10B|   78|
|  4| Dewi|  10B|   92|
+---+-----+-----+-----+

jumlah baris: 4

Properti columns memberi daftar nama kolom, sedangkan dtypes memberi daftar pasangan nama dan tipe dalam format PySpark, terlihat dari tipe bigint yang setara dengan long pada string DDL. Metode printSchema() mencetak schema sebagai pohon dengan penanda nullable, yaitu indikator apakah kolom boleh berisi nilai kosong. Metode show() mencetak sebagian baris dalam format tabel dengan lebar kolom otomatis, dan count() mengembalikan jumlah baris sebagai bilangan Python. orderBy("id") sebelum show() menjamin urutan baris keluaran selalu sama pada setiap eksekusi.

Baris tunggal, akses kolom, dan statistik ringkas#

Skrip kedua menelusuri satu baris, satu kolom, dan ringkasan statistik satu kolom numerik.

python baris_dan_kolom.py
from pyspark.sql import SparkSession

spark = (
    SparkSession.builder
    .master("local[2]")
    .appName("pengamal-baris-kolom")
    .config("spark.sql.shuffle.partitions", "2")
    .getOrCreate()
)

data = [
    (1, "Ali", "10A", 85),
    (2, "Budi", "10A", 90),
    (3, "Citra", "10B", 78),
    (4, "Dewi", "10B", 92),
]
df = spark.createDataFrame(data, "id long, nama string, kelas string, nilai long")

print("=== baris pertama sebagai Row ===")
baris = df.first()
print(baris)
print("nama:", baris["nama"])
print("nilai:", baris["nilai"])

print('=== akses satu kolom df["nilai"] ===')
kolom = df["nilai"]
print(type(kolom).__name__)
df.select("nama", kolom).orderBy("nilai").show()

print("=== describe satu kolom numerik ===")
df.describe("nilai").orderBy("summary").show()

spark.stop()
text Keluaran
WARNING: Using incubator modules: jdk.incubator.vector
=== baris pertama sebagai Row ===
Row(id=1, nama='Ali', kelas='10A', nilai=85)
nama: Ali
nilai: 85
=== akses satu kolom df["nilai"] ===
Column
+-----+-----+
| nama|nilai|
+-----+-----+
|Citra|   78|
|  Ali|   85|
| Budi|   90|
| Dewi|   92|
+-----+-----+

=== describe satu kolom numerik ===
+-------+-----------------+
|summary|            nilai|
+-------+-----------------+
|  count|                4|
|    max|               92|
|   mean|            86.25|
|    min|               78|
| stddev|6.238322424070967|
+-------+-----------------+

Keluaran print(baris) memperlihatkan objek Row dengan pasangan nama dan nilainya, dan baris["nama"] mengambil satu nilai dari baris tersebut. Ekspresi df["nilai"] ternyata tidak mengembalikan nilai, melainkan objek Column, yaitu representasi kolom yang dipakai untuk menyusun ekspresi seperti pada df.select("nama", kolom). Metode describe("nilai") menghasilkan ringkasan count, mean, stddev, min, dan max untuk kolom numerik. Pengurutan dengan orderBy("summary") menjadikan baris ringkasan tercetak dalam urutan alfabetis yang tetap, karena urutan keluaran show() tanpa pengurutan tidak dijamin sama antar eksekusi.

Kesalahan umum#

Kesalahan pertama adalah menganggap df["nilai"] sebagai cara mengambil nilai. Ekspresi itu menghasilkan objek Column untuk menyusun transformasi. Nilai yang benar-benar bisa dipakai oleh Python baru diperoleh setelah baris dibawa ke driver melalui first() atau collect(), lalu diakses dengan baris["nilai"].

Kesalahan kedua adalah menilai data dari tampilan show(). Metode ini memotong teks panjang dengan tanda elipsis dan menampilkan maksimal 20 baris secara default, sehingga data yang tampak terpotong padahal utuh, atau data yang tampak hilang padahal hanya di luar 20 baris pertama. Untuk memastikannya, gunakan show(n, truncate=False) atau periksa dengan count().

Kesalahan ketiga adalah mengira transformasi mengubah DataFrame asal. Pemanggilan df.orderBy("id") tanpa menyimpan hasilnya tidak mengubah df sama sekali karena DataFrame bersifat immutable. Pola yang benar adalah menyimpan hasil ke variabel baru, misalnya df_urut = df.orderBy("id"), sebelum variabel itu dipakai atau dicetak.

Ringkasan#

  • DataFrame terdiri atas baris dan kolom bertipe, dan daftar nama beserta tipe kolomnya membentuk schema.
  • spark.createDataFrame(data, "tipe-kolom") membuat DataFrame dari data literal dengan schema eksplisit.
  • columns, dtypes, dan printSchema() menginspeksi struktur, sedangkan show(), count(), dan describe() memeriksa isi.
  • df["nilai"] menghasilkan objek Column, baris yang dibawa ke driver berbentuk objek Row, dan seluruh transformasi bersifat deklaratif, immutable, serta lazy.

Referensi#