Ana içeriğe atla

Veri Mühendisleri için DuckDB: Veri Boru Hatlarınızı 10 Kat ve Üzeri Hızlandırın

DuckDB, dizüstü bilgisayarınızda çalışan güçlü bir analitik motordur. Veri okuma ve işlemeyi hızlandırmak ve boru hattı çalışma sürelerini dakikalardan saniyelere indirmek için kullanabilirsiniz. Öğrenmek için bu uygulamalı kılavuzu izleyin.
Güncel 3 Eki 2026  · 13 dk. oku

Yapay Zeka ile Keşfedin

ChatGPTClaudePerplexity

Yöneticiniz boru hattınızın ne kadar sürede çalıştığını kaç kez sordu ve yanıt vermekten utandınız?

Yalnız değilsiniz. Python’ın pratikliği bir yerde bedelini ödetir. Bu da genellikle çalışma hızıdır. Boru hatları yazması hızlı ama ölçeklemesi zordur; proje gereksinimleri arttıkça giderek daha fazla zaman alırlar.

Ama belki darboğaz kodunuz değildir. Belki de pandas’tan sıkıştırabileceğiniz ek bir performans yoktur. Belki de farklı bir veri işleme motoruna geçmek, çalışma süresini saatlerden dakikalara indirebilir.

İşte DuckDB tam burada devreye girer.

Bu yazıda size DuckDB’nin tam olarak ne olduğunu ve neden veri mühendisleri için önemli olduğunu anlatacağım. Uygulamalı örneklerle DuckDB’yi nasıl kullanacağınızı öğrenecek ve Python’un en popüler veri işleme kütüphanelerinden ne kadar daha hızlı olduğunu göreceksiniz. 

DuckDB nedir ve Veri Mühendisleri için Sıradaki Büyük Şey mi?

DuckDB, açık kaynaklı, gömülü, süreç içi, ilişkisel bir OLAP VTYS’dir.

Düz Türkçesiyle, bellekte çalışan analitik bir kolonsal veritabanı olarak düşünebilirsiniz. Analitik veritabanı olduğu için INSERT ve UPDATE yerine SELECT ifadeleri için optimize edilmiştir. SQLite gibidir, ama tam tersi yönde.

DuckDB yaklaşık 5 yıldır ortalıkta, ancak ilk “stabil” sürüm Haziran 2024’te duyuruldu - bu yazının yazıldığı tarihten 3 ay önce. Ancak yenilik faktörü sizi yanıltmasın - DuckDB pek çok kişi tarafından denenip test edildi ve hızın kritik olduğu durumlarda neredeyse her zaman önerilen bir araç.

Veri boru hatlarınız için neden DuckDB’yi seçmelisiniz

Eğer bir veri mühendisi iseniz, DuckDB’yi veri boru hatlarınızda kullanmanın somut faydalarından bazıları şunlardır:

  • Hızlıdır: Python’ın varsayılan veri çerçevesi kütüphanelerinden katbekat daha hızlı düşünün. Çoğu durumda hız için optimize edilmiş kütüphanelerden (ör. polars) bile daha hızlıdır.
  • Açık kaynaklıdır: Tüm kaynak kodu herkese açık bir GitHub deposunda mevcuttur. Kullanabilir, üzerinde oynayabilir ve hatta projeye katkıda bulunabilirsiniz.
  • Buluta geçiş noktasını geciktirir: Bulut bilişim pahalı olabilir, ancak hızın kritik olduğu durumlarda çoğu zaman bu bedel haklıdır. DuckDB ile dizüstü bilgisayarınızda yüz milyonlarca satırı analiz edebilirsiniz.
  • Öğrenmesi kolaydır: DuckDB ile başlamak kolaydır. DuckDB, Python’ın veri işleme kütüphaneleriyle iyi entegre olduğundan, mevcut veri boru hatlarınızda muhtemelen asgari değişiklik yapmanız yeterli olacaktır.
  • Bulut depolama platformlarıyla iyi entegre olur: Örneğin, veriyi doğrudan AWS S3’ten okuyan bir DuckDB SQL sorgusu yazabilirsiniz. Önce veriyi indirmenize gerek yoktur.

Bu nedenlerle (ve aşağıda göreceğiniz daha pek çoğuyla) DuckDB’nin veri mühendisleri için sıradaki büyük sıçrama olduğunu düşünüyorum.

DuckDB ile Nasıl Başlanır

Daha önce söylediğim gibi, DuckDB dizüstü bilgisayarınızda çalışır. İlk iş onu oraya kurmaktır. 

Kurulum adımlarını macOS için göstereceğim, ancak Windows ve Linux için olan talimatlar da iyi açıklanmış ve kolay takip edilir.

Adım 1: DuckDB’yi kurun

Bir Mac’iniz varsa, DuckDB’yi kurmanın en kolay yolu Homebrew kullanmaktır. Aşağıdaki kabuk komutunu çalıştırın:

brew install duckdb

Birkaç saniye içinde aşağıdaki çıktıyı göreceksiniz:

Homebrew ile DuckDB kurulumu

Homebrew ile DuckDB kurulumu

Kurulduktan sonra, aşağıdaki komutla DuckDB kabuğuna girebilirsiniz:

duckdb

DuckDB kabuğu

DuckDB kabuğu

Buradan sonrası artık sizin maharetinize kalmış!

SQL sorgularını doğrudan konsoldan yazıp çalıştırabilirsiniz. Örneğin, 2024’ün ilk 6 ayına ait NYC Taxi Data’yı indirdim. Siz de aynı şeyi yaptıysanız, tek bir Parquet dosyasının satır sayısını göstermek için aşağıdaki parçacığı kullanın:

SELECT COUNT(*)
  FROM PARQUET_SCAN("path-to-data.parquet");

Tek Parquet dosyası satır sayıları

Tek Parquet dosyası satır sayıları

Evet - tek bir dosyadan neredeyse 20 milyon satır. Ben bunlardan 24 tane indirdim.

Muhtemelen DuckDB’yi terminalden çalıştırmak istemezsiniz; bu yüzden sırada DuckDB’yi Python ile bağlama adımlarını göstereceğim.

Adım 2: DuckDB’yi Python iş akışınıza bağlayın

Bu bölümün amacı, DuckDB’yi veri mühendisliğinin en popüler dili olan Python’da nasıl yapılandırıp çalıştıracağınızı göstermek.

Hatta DataCamp, Python ile veri mühendisliği üzerine eksiksiz bir kariyer yolu sunuyor.

Python’ın önce kurmanız gereken özel bir DuckDB kütüphanesi vardır. Tercihen sanal bir ortamda, terminalden aşağıdakini çalıştırın:

pip install duckdb

Şimdi yeni bir Python dosyası oluşturup aşağıdaki kodu yapıştırın:

import duckdb
# Function to get the count from a single file
def get_row_count_from_file(conn: duckdb.DuckDBPyConnection, file_path: str) -> int:
    query = f"""
        SELECT COUNT(*)
        FROM PARQUET_SCAN("{file_path}")
    """
    return conn.sql(query).fetchone()[0]
if __name__ == "__main__":
    # In-memory database connection
    conn = duckdb.connect()
    # Path to a parquet file
    file_path = "fhvhv_tripdata_2024-01.parquet"
    # Get the row count
    row_count = get_row_count_from_file(conn=conn, file_path=file_path)
    print(row_count)

Kısaca, bu kod parçasında geçerli bir DuckDB bağlantısı ve dosya yolu verildiğinde tek bir dosyadaki satır sayısını getiren bir fonksiyon bulunur.

Betik çalıştırıldıktan sonra çıktıyı neredeyse anında alacaksınız:

DuckDB ve Python bağlantısı

DuckDB ve Python bağlantısı

Hepsi bu! 

Sırada, Python ve DuckDB ile nasıl inanılmaz şeyleri ışık hızında yapabileceğinizi göstereceğim!

DuckDB İş Başında: Veri Boru Hatlarını Nasıl Hızlandırırsınız

Referans olması için, kodu NYC Taxi Dataset ’in 2024 (Ocak - Haziran) bölümünde, yüksek hacimli kiralık araçlar için çalıştıracağım. Bu 6 Parquet dosyası ve yaklaşık 3GB disk alanı kaplıyor. Donanım olarak 12 CPU çekirdekli ve 36GB birleşik belleğe sahip 16" M3 Pro MacBook Pro kullanıyorum.

Sizin sonuçlarınız değişebilir, ama yine de benzer sayılara ulaşmalısınız.

Devasa veri kümelerini anında okuyun

Bu 6 dosyayı iki ek formata - CSV ve JSON - dönüştürdüm. Her birinin ne kadar disk alanı kapladığını görebilirsiniz:

Veri kümesi boyutu karşılaştırması

Veri kümesi boyutu karşılaştırması

Toplam: Parquet için 2,96GB, CSV için 19,31GB ve JSON için 66,04GB.

Muazzam fark! Bugünkü yazıdan başka bir şey almazsanız bile, şunu unutmayın: büyük dosyalarla çalışırken her zaman Parquet dosya formatını kullanın. Hem işlem süresinden hem de disk alanından tasarruf sağlayacaktır.

DuckDB, aynı formatta birden fazla dosyayı (glob deseni kullanarak) bir kerede okuyabilen kullanışlı fonksiyonlara sahiptir. Altısını birden okuyup satır sayısını alacak ve çalışma sürelerini karşılaştıracağım:

import duckdb
import pandas as pd
from datetime import datetime
def get_row_count_and_measure_time(file_format: str) -> str:
    # Construct a DuckDB query based on the file_format
    match file_format:
        case "csv":
            query = """
            SELECT COUNT(*)
            FROM READ_CSV("nyc-taxi-data-csv/*.csv")
            """
        case "json":
            query = """
            SELECT COUNT(*)
            FROM READ_JSON("nyc-taxi-data-json/*.json")
            """
        case "parquet":
            query = """
                SELECT COUNT(*)
                FROM READ_PARQUET("nyc-taxi-data/*.parquet")
            """
        case _:
            raise KeyError("Param file_format must be in [csv, json, parquet]")
    # Open the database connection and measure the start time
    time_start = datetime.now()
    conn = duckdb.connect()
    # Get the row count
    row_count = conn.sql(query).fetchone()[0]
    # Close the database connection and measure the finish time
    conn.close()
    time_end = datetime.now()
    return {
        "file_format": file_format,
        "duration_seconds": (time_end - time_start).seconds + ((time_end - time_start).microseconds) / 1000000,
        "row_count": row_count
    }
# Run the function
file_format_results = []
for file_format in ["csv", "json", "parquet"]:
    file_format_results.append(get_row_count_and_measure_time(file_format=file_format))
pd.DataFrame(file_format_results)

Sonuçlar geldi - yalnızca tek bir net kazanan var:

Dosya okuma çalışma süresi karşılaştırması

Dosya okuma çalışma süresi karşılaştırması

DuckDB sayesinde üçü de hızlı ve aynı noktada buluşuyor, ancak Parquet’in CSV’ye kıyasla 600 kat, JSON’a kıyasla ise 1200 kat kazandığı açık.

Bu nedenle, yazının geri kalanında yalnızca Parquet kullanacağım.

SQL ile veri sorgulama

DuckDB, veriyi SQL ile birleştirmenize olanak tanır.

Bu bölümde amacım bu fikri genişletmek. Daha açık olmak gerekirse, 120 milyon satırdan oluşan bir veriden aylık düzeyde özet istatistikleri - yolculuk sayısı, yolculuk süresi, mesafe, maliyet ve sürücü ödemesi bilgilerini - nasıl hesaplayacağınızı göstereceğim.

En iyi yanı: Sadece iki saniye sürecek!

Aşağıda, veriyi birleştirmek ve sonuçları yazdırmak için bir Python betiğinde kullandığım kod yer alıyor:

conn = duckdb.connect()
query = """
    SELECT
        ride_year || '-' || ride_month AS ride_period,
        COUNT(*) AS num_rides,
        ROUND(SUM(trip_time) / 86400, 2) AS ride_time_in_days,
        ROUND(SUM(trip_miles), 2) AS total_miles,
        ROUND(SUM(base_passenger_fare + tolls + bcf + sales_tax + congestion_surcharge + airport_fee + tips), 2) AS total_ride_cost,
        ROUND(SUM(driver_pay), 2) AS total_rider_pay
    FROM (
        SELECT
            DATE_PART('year', pickup_datetime) AS ride_year,
            DATE_PART('month', pickup_datetime) AS ride_month,
            trip_time,
            trip_miles,
            base_passenger_fare,
            tolls,
            bcf,
            sales_tax,
            congestion_surcharge,
            airport_fee,
            tips,
            driver_pay
        FROM PARQUET_SCAN("nyc-taxi-data/*.parquet")
        WHERE 
            ride_year = 2024
        AND ride_month >= 1 
        AND ride_month <= 6
    )
    GROUP BY ride_period
    ORDER BY ride_period
"""
# Aggregate data, print it, and show the data type
results = conn.sql(query)
results.show()
print(type(results))

Çıktı; birleştirme sonuçlarını, results değişkeninin türünü ve toplam çalışma süresini içerir:

Aylık özet istatistiklerin birleştirilmesi

Aylık özet istatistiklerin birleştirilmesi

Şunu bir sindirin: 120 milyondan fazla satıra yayılan 3GB veri, bir dizüstü bilgisayarda 2 saniyede birleştirildi!

Tek sorun, değişken türünün uzun vadede pek kullanışlı olmaması. Neyse ki kolay bir çözüm var.

pandas ve DataFrame’lerle entegrasyon

DuckDB’yi kullanmanın artısı sadece hızlı olması değil, aynı zamanda favori veri çerçevesi kütüphaneniz olan pandas ile de iyi entegre olmasıdır.

Yukarıda gösterdiğime benzer geçici bir sonuç kümeniz varsa, onu bir pandas DataFrame’ine dönüştürmek için yalnızca .df() metodunu çağırmanız yeterlidir:

# Convert to Pandas DataFrame
results_df = results.df()
# Print the type and contents
print(type(results_df))
results_df

DuckDB'den pandas'a dönüştürme

DuckDB'den pandas'a dönüştürme

Benzer şekilde, DuckDB’yi, halen belleğe yüklenmiş pandas DataFrame’lerinde hesaplamalar yapmak için kullanabilirsiniz.

Püf noktası, DuckDB SQL sorgusunda FROM ifadesinden sonra değişken adını referans etmektir. İşte bir örnek:

# Pandas DataFrame
pandas_df = pd.read_parquet("nyc-taxi-data/fhvhv_tripdata_2024-01.parquet")
# Run SQL queries through DuckDB
duckdb_res = duckdb.sql("""
    SELECT
        pickup_datetime,
        dropoff_datetime,
        trip_miles,
        trip_time,
        driver_pay
    FROM pandas_df
    WHERE trip_miles >= 300
""").df()
duckdb_res

Mevcut bir Pandas DataFrame üzerinde DuckDB sorgusu

Mevcut bir pandas DataFrame üzerinde DuckDB sorgusu

pandas’ı tamamen devre dışı bırakabilir veya mevcut pandas DataFrame’leri üzerinde birleştirmeleri daha hızlı gerçekleştirebilirsiniz.

Sırada, birkaç gelişmiş DuckDB özelliğini göstereceğim.

Veri Mühendisleri için İleri Düzey DuckDB

DuckDB, göründüğünden çok daha fazlasını sunar.

Bu bölümde, veri mühendisleri için hayati öneme sahip birkaç ileri düzey DuckDB özelliğini anlatacağım.

Eklentiler

DuckDB, yerel işlevselliğini çekirdek ve topluluk eklentileri aracılığıyla genişletmenize olanak tanır. Bulut depolama sistemlerine ve veritabanlarına bağlanmak ve ek dosya formatlarıyla çalışmak için eklentileri sıkça kullanıyorum.

Eklentileri hem Python hem de DuckDB konsollarından kurabilirsiniz.

Aşağıdaki kod parçasında, AWS’ye bağlanmak ve S3 verisini okumak için gerekli olan httpfs eklentisinin nasıl kurulacağını gösteriyorum:

import duckdb
conn = duckdb.connect()
conn.execute("""
    INSTALL httpfs;
    LOAD httpfs;
""")

conn.execute()’a .df() metodunu zincirlemezseniz herhangi bir çıktı görmezsiniz. Zincirlerseniz “Success” veya “Error” mesajı görürsünüz.

Bulut depolama sorgulama

Çoğu zaman, boru hatlarınıza dahil etmeniz gereken verilere şirketiniz erişim verir. Bunlar genellikle AWS S3 gibi ölçeklenebilir bulut platformlarında depolanır.

DuckDB, S3’e (ve diğer platformlara) doğrudan bağlanmanıza olanak tanır.

Eklentinin (httpfs) nasıl kurulacağını zaten gösterdim; geriye yalnızca yapılandırmak kalıyor. AWS, bölgeniz, erişim anahtarınız ve gizli erişim anahtarınız hakkında bilgi vermenizi ister.

Bunların hepsine sahip olduğunuzu varsayarak, aşağıdaki komutu Python üzerinden çalıştırın:

conn.execute("""    CREATE SECRET aws_s3_secret (
        TYPE S3,
        KEY_ID '<your-access-key>',
        SECRET '<your-secret-key>',
        REGION '<your-region>'
    );
""")

S3 kovamda New York Taxi veri kümesinden iki dosya bulunuyor:

S3 kova içeriği

S3 kova içeriği

Dosyaları indirmenize gerek yok; S3’ten doğrudan tarayabilirsiniz:

import duckdb
conn = duckdb.connect()
aws_count = conn.execute("""
    SELECT COUNT(*)
    FROM PARQUET_SCAN('s3://<your-bucket-name>/*.parquet');
""").df()
aws_count

DuckDB S3 okuma sonuçları

DuckDB S3 okuma sonuçları

Doğru okuyorsunuz: S3 kovasında duran ~ 900MB veriyi taramak yalnızca 4 saniye sürdü.

Paralel işleme

Paralelleştirme söz konusu olduğunda, DuckDB bunu varsayılan olarak satır gruplarına göre uygular: Parquet’te tipik olarak bulunan yatay veri bölümleri. Tek bir satır grubu en fazla 122.880 satır içerebilir. 

DuckDB’de paralellik, 122.880 satırdan fazlasıyla çalışmaya başladığınızda devreye girer.

Bu gerçekleştiğinde DuckDB otomatik olarak yeni iş parçacıkları başlatır. Varsayılan olarak, iş parçacığı sayısı CPU çekirdeği sayısına eşittir. Ancak bu, iş parçacığı sayısıyla manuel olarak oynamanıza engel değildir.

Bu bölümde, bunu nasıl yapacağınızı ve aynı iş yükünde farklı iş parçacığı sayısının tam etkisini göstereceğim.

Geçerli iş parçacığı sayısını görüntülemek için şunu çalıştırın:

SELECT current_setting('threads') AS threads;

DuckDB tarafından kullanılan geçerli iş parçacığı sayısı

DuckDB tarafından kullanılan geçerli iş parçacığı sayısı

Aynı sonucu Python üzerinden de alabilirsiniz.

İş parçacığı sayısını değiştirmek için SET threads = N komutunu çalıştırmak isteyeceksiniz. 

Aşağıdaki kod parçasında, DuckDB sorgusunu verilen sayıda iş parçacığıyla çalıştıran, sonucu bir pandas DataFrame’ine dönüştüren ve çalışma süresini (diğer şeylerin yanı sıra) döndüren özel bir Python fonksiyonunun nasıl uygulanacağını gösteriyorum. Fonksiyonun altındaki kod, onu [1, 12] iş parçacığı aralığında çalıştırır:

def thread_test(n_threads: int) -> dict:
    # Open the database connection and measure the start time
    time_start = datetime.now()
    conn = duckdb.connect()
    # Set the number of threads
    conn.execute(f"SET threads = {n_threads};")
    query = """
        SELECT
            ride_year || '-' || ride_month AS ride_period,
            COUNT(*) AS num_rides,
            ROUND(SUM(trip_time) / 86400, 2) AS ride_time_in_days,
            ROUND(SUM(trip_miles), 2) AS total_miles,
            ROUND(SUM(base_passenger_fare + tolls + bcf + sales_tax + congestion_surcharge + airport_fee + tips), 2) AS total_ride_cost,
            ROUND(SUM(driver_pay), 2) AS total_rider_pay
        FROM (
            SELECT
                DATE_PART('year', pickup_datetime) AS ride_year,
                DATE_PART('month', pickup_datetime) AS ride_month,
                trip_time,
                trip_miles,
                base_passenger_fare,
                tolls,
                bcf,
                sales_tax,
                congestion_surcharge,
                airport_fee,
                tips,
                driver_pay
            FROM PARQUET_SCAN("nyc-taxi-data/*.parquet")
            WHERE 
                ride_year = 2024
            AND ride_month >= 1 
            AND ride_month <= 6
        )
        GROUP BY ride_period
        ORDER BY ride_period
    """
    # Convert to DataFrame
    res = conn.sql(query).df()
    # Close the database connection and measure the finish time
    conn.close()
    time_end = datetime.now()
    return {
        "num_threads": n_threads,
        "num_rows": len(res),
        "duration_seconds": (time_end - time_start).seconds + ((time_end - time_start).microseconds) / 1000000
    }
thread_results = []
for n_threads in range(1, 13):
    thread_results.append(thread_test(n_threads=n_threads))
pd.DataFrame(thread_results)

Benimkine benzer bir çıktı görmelisiniz:

Farklı iş parçacığı sayıları için DuckDB çalışma süresi

Farklı iş parçacığı sayıları için DuckDB çalışma süresi

Genel olarak, bir hesaplamaya ne kadar çok iş parçacığı atarsanız o kadar hızlı tamamlanır. Yeni bir iş parçacığı oluşturma ek yükünün toplam çalışma süresini artırdığı bir nokta olabilir, ancak bu aralıkta böyle bir durumla karşılaşmadım.

Performans Karşılaştırması: DuckDB vs. Geleneksel Yaklaşımlar

Bu bölümde sıfırdan nasıl bir veri boru hattı yazacağınızı göstereceğim. Oldukça basit olacak: diskten veri okumak, birleştirmeler yapmak ve sonuçları yazmak. Amacım, pandas’tan DuckDB’ye geçerek ne kadar performans kazanabileceğinizi göstermek. Üstelik kodunuz daha temiz görünecek.

Bu, ETL/ELT süreçlerine kapsamlı bir giriş değil, üst düzey bir genel bakıştır.

Veri boru hattı hedefleri

Göreceğiniz veri boru hattı şunları gerçekleştirir:

  • Extract (Çıkar): Diskten birden fazla Parquet dosyasını okur (yaklaşık 120 milyon satır).
  • Transform (Dönüştür): Taksi şirketi geliri, kâr marjı (yolculuk maliyeti ile sürücü ödemesi farkı) ve yolculuk başına ortalama gelir gibi aylık özet istatistikleri hesaplar. Yazının önceki kısımlarında ele aldığım daha yaygın istatistikleri de göreceksiniz.
  • Load (Yükle): Aylık istatistikleri yerelde bir CSV dosyasına kaydeder.

Kodu çalıştırdıktan sonra, her iki boru hattı uygulaması için de (bazı yuvarlama farklılıkları hariç) tam olarak aynı birleştirme sonuçlarını elde etmelisiniz:

DuckDB ve Pandas için boru hattı sonuçları

DuckDB ve pandas için boru hattı sonuçları

Önce, pandas ile boru hattı uygulamasını inceleyelim.

Kod: Python ve pandas

Ne yaparsam yapayım, 6 Parquet dosyasının hepsini bir anda işleyemedim. Birkaç saniye içinde şu tür sistem uyarı mesajları görüntülendi:

Sistem bellek hatası

Sistem bellek hatası

Görünüşe göre 36GB bellek, 120 milyon satırı bir seferde işlemek için yeterli değil. Sonuç olarak Python betiği sonlandırıldı:

Yetersiz bellek nedeniyle sonlandırılan Python betiği

Yetersiz bellek nedeniyle sonlandırılan Python betiği

Bunu azaltmak için, Parquet dosyalarını sıralı olarak işlemek zorunda kaldım. Veri boru hattı için kullandığım kod şuydu:

import os
import pandas as pd
def calculate_monthly_stats_per_file(file_path: str) -> pd.DataFrame:
    # Read a single Parquet file
    df = pd.read_parquet(file_path)
    # Extract ride year and month
    df["ride_year"] = df["pickup_datetime"].dt.year
    df["ride_month"] = df["pickup_datetime"].dt.month
    # Remove data points that don"t fit in the time period
    df = df[(df["ride_year"] == 2024) & (df["ride_month"] >= 1) & (df["ride_month"] <= 6)]
    # Combine ride year and month
    df["ride_period"] = df["ride_year"].astype(str) + "-" + df["ride_month"].astype(str)
    # Calculate total ride cost
    df["total_ride_cost"] = (
        df["base_passenger_fare"] + df["tolls"] + df["bcf"] +
        df["sales_tax"] + df["congestion_surcharge"] + df["airport_fee"] + df["tips"]
    )
    # Aggregations
    summary = df.groupby("ride_period").agg(
        num_rides=("pickup_datetime", "count"),
        ride_time_in_days=("trip_time", lambda x: round(x.sum() / 86400, 2)),
        total_miles=("trip_miles", "sum"),
        total_ride_cost=("total_ride_cost", "sum"),
        total_rider_pay=("driver_pay", "sum")
    ).reset_index()
    # Additional attributes
    summary["total_miles_in_mil"] = summary["total_miles"] / 1000000
    summary["company_revenue"] = round(summary["total_ride_cost"] - summary["total_rider_pay"], 2)
    summary["company_margin"] = round((1 - (summary["total_rider_pay"] / summary["total_ride_cost"])) * 100, 2).astype(str) + "%"
    summary["avg_company_revenue_per_ride"] = round(summary["company_revenue"] / summary["num_rides"], 2)
    # Remove columns that aren't needed anymore
    summary.drop(["total_miles"], axis=1, inplace=True)
    return summary
def calculate_monthly_stats(file_dir: str) -> pd.DataFrame:
    # Read data from multiple Parquet files
    files = [os.path.join(file_dir, f) for f in os.listdir(file_dir) if f.endswith(".parquet")]
    
    df = pd.DataFrame()
    for file in files:
        print(file)
        file_stats = calculate_monthly_stats_per_file(file_path=file)
        # Check if df is empty
        if df.empty:
            df = file_stats
        else:
            # Concat row-wise
            df = pd.concat([df, file_stats], axis=0)
    # Sort the dataset
    df = df.sort_values(by="ride_period")
    # Change column order
    cols = ["ride_period", "num_rides", "ride_time_in_days", "total_miles_in_mil", "total_ride_cost",
            "total_rider_pay", "company_revenue", "company_margin", "avg_company_revenue_per_ride"]
    return df[cols]
if __name__ == "__main__":
    data_dir = "nyc-taxi-data"
    output_dir = "pipeline_results"
    output_file_name = "results_pandas.csv"
    # Run the pipeline
    monthly_stats = calculate_monthly_stats(file_dir=data_dir)
    # Save to CSV
    monthly_stats.to_csv(f"{output_dir}/{output_file_name}", index=False)

DuckDB ile boru hattı uygulaması umarım bellek sorunları olmadan tamamlanacaktır.

Kod: DuckDB

DuckDB kodunun büyük kısmına zaten aşinasınız.

Yeni olan tek şey, birkaç ek istatistiği hesaplayan en üstteki SELECT. Diğer her şeyi değiştirmedim:

import duckdb
import pandas as pd
def calculate_monthly_stats(file_dir: str) -> pd.DataFrame:
    query = f"""
        SELECT
            ride_period,
            num_rides,
            ride_time_in_days,
            total_miles / 1000000 AS total_miles_in_mil,
            total_ride_cost,
            total_rider_pay,
            ROUND(total_ride_cost - total_rider_pay, 2) AS company_revenue,
            ROUND((1 - total_rider_pay / total_ride_cost) * 100, 2) || '%' AS company_margin,
            ROUND((total_ride_cost - total_rider_pay) / num_rides, 2) AS avg_company_revenue_per_ride
        FROM (
            SELECT
                ride_year || '-' || ride_month AS ride_period,
                COUNT(*) AS num_rides,
                ROUND(SUM(trip_time) / 86400, 2) AS ride_time_in_days,
                ROUND(SUM(trip_miles), 2) AS total_miles,
                ROUND(SUM(base_passenger_fare + tolls + bcf + sales_tax + congestion_surcharge + airport_fee + tips), 2) AS total_ride_cost,
                ROUND(SUM(driver_pay), 2) AS total_rider_pay
            FROM (
                SELECT
                    DATE_PART('year', pickup_datetime) AS ride_year,
                    DATE_PART('month', pickup_datetime) AS ride_month,
                    trip_time,
                    trip_miles,
                    base_passenger_fare,
                    tolls,
                    bcf,
                    sales_tax,
                    congestion_surcharge,
                    airport_fee,
                    tips,
                    driver_pay
                FROM PARQUET_SCAN("{file_dir}/*.parquet")
                WHERE 
                    ride_year = 2024
                AND ride_month >= 1 
                AND ride_month <= 6
            )
            GROUP BY ride_period
            ORDER BY ride_period
        )
    """
    conn = duckdb.connect()
    df = conn.sql(query).df()
    conn.close()
    return df
if __name__ == "__main__":
    data_dir = "nyc-taxi-data"
    output_dir = "pipeline_results"
    output_file_name = "results_duckdb.csv"
    # Run the pipeline
    monthly_stats = calculate_monthly_stats(file_dir=data_dir)
    # Save to CSV
    monthly_stats.to_csv(f"{output_dir}/{output_file_name}", index=False)

Sırada, çalışma süresi farklarını göstereceğim.

Performans karşılaştırması sonuçları

Her iki boru hattını da 5 kez çalıştırıp sonuçların ortalamasını aldıktan sonra elde ettiğim çalışma süreleri şunlar:

DuckDB vs pandas çalışma süresi karşılaştırma grafiği

DuckDB vs. pandas çalışma süresi karşılaştırma grafiği

pandas, ortalama olarak DuckDB’den 24 kat daha yavaştı; 6 Parquet dosyasına yayılan yaklaşık 120 milyon satırı (~3GB) yüklerken ve işlerken.

Karşılaştırma tamamen adil değil; çünkü pandas ile tüm Parquet dosyalarını bir kerede işleyemedim. Yine de sonuçlar geçerli, çünkü iş yerinde benzer zorluklarla karşılaşacaksınız. 

Bir kütüphane ihtiyacınızı karşılamıyorsa, başka birini deneyin. Ve çoğu zaman en hızlı bitirecek olan muhtemelen DuckDB olacaktır.

Veri Boru Hatlarında DuckDB Kullanımı için En İyi Uygulamalar

Sizi DuckDB’yi kendi başınıza daha fazla keşfetmeye bırakmadan önce, birkaç genel ve veri mühendisliğiyle ilgili en iyi uygulamayı paylaşmak istiyorum:

  • Önce her zaman DuckDB’nin yerleşik fonksiyonlarını kullanmayı deneyin: Python fonksiyonlarınızı DuckDB’ye kullanıcı tanımlı fonksiyonlar ile taşıyabilir, bu da DuckDB’nin esnekliğini bambaşka bir boyuta taşır. Programlamadaki genel en iyi uygulamalardan biri tekerleği yeniden icat etmemektir. Başka bir deyişle, bir işlevsellik halihazırda varsa onu sıfırdan uygulamak istemezsiniz.
  • Önce dosya formatlarını optimize edin: DuckDB kutudan çıktığı gibi ciddi performans avantajları sunsa da, optimizasyonları kısa kesmeniz gerektiği anlamına gelmez. CSV dosyalarını okurken DuckDB’nin Parquet’e kıyasla ne kadar yavaş kaldığını (600 kata kadar) gördünüz. Parquet her zaman daha hızlı okunur ve daha az disk alanı kaplar. Kazan-kazan. 
  • DuckDB’yi işlemsel amaçlar için kullanmayın: DuckDB bir OLAP veritabanıdır (Çevrimiçi Analitik İşleme), yani SELECT ifadeleri için optimize edilmiştir. Sık INSERT ve UPDATE ifadelerine dayanan iş akışlarında kullanmaktan kaçınmalısınız. Böyle bir durumda, SQLite kullanın.
  • Jupyter Notebook’lardaki yerel DuckDB desteğinden yararlanın: Jupyter Lab/Notebook kullanıcıları DuckDB sorgularını doğrudan çalıştırabilir, özel Python fonksiyonlarını kullanmaya gerek kalmadan. Bu, veriyi daha hızlı keşfetmenin ve not defterlerinizi derli toplu tutmanın harika bir yoludur.
  • DuckDB’nin eşzamanlılığı nasıl ele aldığını her zaman hatırlayın: DuckDB’yi, bir işlemin veritabanını okumasına ve yazmasına izin verecek şekilde veya birden fazla işlemin veritabanını okumasına ancak hiçbirinin yazmamasına izin verecek şekilde yapılandırabilirsiniz. İlki, daha hızlı analitik sorgular için verinin RAM’de önbelleğe alınmasını sağlar. Teoride birden fazla işlem aynı veritabanı dosyasına yazabilir, ancak bunu başarmak için süreçler arası kilitler ve veritabanını açma/kapama mantığını manuel olarak uygulamanız gerekir.
  • DuckDB’yi bulutta kullanabilirsiniz: MotherDuck projesi, DuckDB’nin gücünü buluta taşıyan ve genişleten işbirlikçi bir veri ambarıdır; veri mühendisleri için keşfetmeye değer bir seçenek olabilir.

Toparlayalım

Sonuç olarak, veri boru hatları kurmak ve optimize etmekten sorumlu bir veri mühendisiyseniz DuckDB’yi denemeniz gerektiğini düşünüyorum.

Kaybedecek hiçbir şeyiniz yok, kazanacak çok şeyiniz var. Şu var ki, neredeyse kesin olarak Python’ın sunduğu her şeyden daha hızlıdır. Dizüstü bilgisayarınızda inanılmaz hızlı çalışır ve aksi halde bellek hatasına yol açacak veri kümelerini işlemenize olanak tanır. Veriyi yerelde saklamak istemediğiniz veya buna izin verilmediği durumlarda bulut depolama platformlarına da bağlanabilir.

Bununla birlikte, masaya getireceğiniz tek optimizasyon DuckDB olmamalıdır. Sisteminize giren veriyi her zaman optimize etmeye çalışın. Örneğin, CSV yerine Parquet kullanmak hem işlem süresinden hem depolamadan tasarruf sağlar. Düşünmeniz gereken bir diğer optimizasyon ise modern veri mimarisi ilkelerini iş akışlarınıza ve boru hatlarınıza uygulamaktır.

DuckDB sadece bir araç. Ama çok güçlü bir araç.

SSS

DuckDB kullanımı ücretsiz mi?

Evet, DuckDB açık kaynaklı bir projedir. İndirebilir, değiştirebilir ve GitHub üzerinden katkıda bulunabilirsiniz.

DuckDB, SQLite&rsquo;tan nasıl farklıdır?

DuckDB, karmaşık SQL sorguları aracılığıyla analitik iş yükleri için optimize edilmiştir (aklınıza SELECT ifadeleri gelsin), SQLite ise işlemsel işlemler için daha uygundur (aklınıza INSERT ve UPDATE ifadeleri gelsin).

DuckDB bir NoSQL veritabanı mı?

Hayır, DuckDB süreç içi bir SQL OLAP (Çevrimiçi Analitik İşleme) veritabanı yönetim sistemidir.

DuckDB, pandas&rsquo;tan daha hızlı mı?

Neredeyse tüm senaryolarda DuckDB, sıklıkla en az bir büyüklük mertebesiyle, pandas’tan daha hızlıdır. Ayrıca pandas’ta bellek hatası çıkaracak veri kümeleriyle çalışmanıza olanak tanır.

DuckDB özel bir SQL lehçesi mi kullanıyor?

Hayır, temel SQL bilginiz varsa hemen uyum sağlarsınız. DuckDB, PostgreSQL lehçesinin kurallarını yakından takip eder; ancak farklı bir veritabanı tedarikçisine alışkın olsanız bile pratikte öğrenme eğrisi yoktur.


Dario Radečić's photo
Author
Dario Radečić
LinkedIn
Hırvatistan merkezli Kıdemli Veri Bilimci. 700’ün üzerinde yayımlanmış makaleyle 10 M+ görüntüleme elde eden, önde gelen bir teknoloji yazarı. TPOT ile Makine Öğrenimi Otomasyonu kitabının yazarı.
Konular
Veri Mühendisliği
Python

Bu kurslarla veri mühendisliği hakkında daha fazlasını öğrenin!

Kurs

Veri Mühendisliğini Anlamak

2 sa
378K
Veri mühendislerinin veri bilimini mümkün kılan temelleri nasıl attığını keşfedin. Kodlama gerektirmez!
Ayrıntıları GörüntüleRight Arrow
Kursa Başla
Devamını GörRight Arrow
İlgili

blog

Hızlı Sevkiyat İçin Pratik Vibe Kodlama Teknoloji Yığını

Ön uç, arka uç, veritabanları, kimlik doğrulama, depolama, e-posta, test, dağıtım ve izleme için en iyi araçları keşfedin.
Abid Ali Awan's photo

Abid Ali Awan

14 dk.

blog

2026’da En Popüler 40 Yazılım Mühendisi Mülakat Sorusu

Algoritmalar, sistem tasarımı ve davranışsal senaryoları kapsayan bu temel sorularla teknik mülakat sürecine hakim olun. Uzman cevapları, kod örnekleri ve kanıtlanmış hazırlık stratejileri edinin.
Dario Radečić's photo

Dario Radečić

15 dk.

Eğitim

Python'da Listeyi String'e Nasıl Dönüştürürsünüz

Bu hızlı eğitimde, Python'da bir listeyi string'e nasıl dönüştüreceğinizi öğrenin.
Adel Nehme's photo

Adel Nehme

Eğitim

.gitignore Nasıl Kullanılır: Örneklerle Pratik Bir Giriş

Git deponuzu temiz tutmak için .gitignore’u nasıl kullanacağınızı öğrenin. Bu eğitim; temelleri, yaygın kullanım durumlarını ve başlamanıza yardımcı olacak pratik örnekleri kapsar!
Kurtis Pykes 's photo

Kurtis Pykes

8 dk.

Devamını GörDevamını Gör