Tablo düşünmek
Anadolu Market'in sipariş verisi bir tablodur: her satır bir sipariş, her sütun bir alan (müşteri, şehir, tutar, tarih). Excel'de yaptığın filtreleme, sıralama, yeni sütun ekleme işlerini kodla ve tekrarlanabilir biçimde yapmanın aracı pandas'tır. İki temel yapı var: Series (tek sütun, etiketli dizi) ve DataFrame (sütunlardan oluşan tablo). DataFrame'in her sütunu bir Series'tir.
Series: etiketli tek boyut
import pandas as pd
stok = pd.Series({"kahve": 120, "çay": 340, "şeker": 15, "un": 0})
print(stok)
print(stok["çay"]) # etiketle erişim
print(stok[stok < 50]) # boolean maske
print(stok.sum(), stok.idxmax())
print((stok * 2).head(2)) # vektörleştirilmiş, numpy gibi
Series'in bir index'i (etiketler) ve values'u (numpy dizisi) vardır. Etiket vermezsen 0, 1, 2... kullanılır.
DataFrame oluşturma ve CSV yükleme
Gerçek işte veri genelde CSV, Excel ya da veritabanından gelir. pd.read_csv bir dosya yolu alır; burada dosya yerine io.StringIO ile bellekteki metni dosya gibi veriyoruz. Aynı fonksiyon, aynı parametreler.
import pandas as pd, io csv_metni = """siparis_id,musteri,sehir,kategori,adet,fiyat,tarih 1001,Ayşe,İstanbul,Gıda,3,45.5,2024-03-01 1002,Mehmet,Ankara,Elektronik,1,899.0,2024-03-01 1003,Ayşe,İstanbul,Gıda,10,12.75,2024-03-02 1004,Zeynep,İzmir,Kozmetik,2,129.9,2024-03-02 1005,Ali,Ankara,Gıda,6,45.5,2024-03-03 1006,Zeynep,İzmir,Elektronik,1,1250.0,2024-03-03 """ df = pd.read_csv(io.StringIO(csv_metni), parse_dates=["tarih"]) print(df.head(3)) print(df.shape, list(df.columns)) print(df.dtypes)
to_datetime çağırmaktan kolaydır. Biçim özelse date_format="%d.%m.%Y" ver.Veriyi tanımak: head, info, describe
Yeni bir veri setine ilk bakışta üç komut: head() ilk satırlar, info() sütun türleri ve eksik sayısı, describe() sayısal özet (ortalama, çeyrekler, min/max). Ek olarak value_counts() kategorik sütunun dağılımını verir.
import pandas as pd, io csv_metni = """siparis_id,musteri,sehir,kategori,adet,fiyat 1001,Ayşe,İstanbul,Gıda,3,45.5 1002,Mehmet,Ankara,Elektronik,1,899.0 1003,Ayşe,İstanbul,Gıda,10,12.75 1004,Zeynep,İzmir,Kozmetik,2,129.9 1005,Ali,Ankara,Gıda,6,45.5 """ df = pd.read_csv(io.StringIO(csv_metni)) df.info() print(df.describe().round(1)) print(df["sehir"].value_counts()) print(df["kategori"].unique(), df["kategori"].nunique())
Seçme: sütun, loc, iloc, maske
Dört yol, dört farklı soru:
| Yazım | Ne seçer | Örnek |
|---|---|---|
df["tutar"] | Tek sütun (Series) | df["sehir"] |
df[["a", "b"]] | Birden çok sütun (DataFrame) | df[["musteri", "sehir"]] |
df.loc[etiket, sütun] | Etiketle satır/sütun | df.loc[2, "sehir"], df.loc[df.adet > 2, ["musteri"]] |
df.iloc[konum, konum] | Sıra numarasıyla | df.iloc[0], df.iloc[:3, 1:3] |
df[maske] | Koşula uyan satırlar | df[df.sehir == "Ankara"] |
import pandas as pd, io csv_metni = """siparis_id,musteri,sehir,kategori,adet,fiyat 1001,Ayşe,İstanbul,Gıda,3,45.5 1002,Mehmet,Ankara,Elektronik,1,899.0 1003,Ayşe,İstanbul,Gıda,10,12.75 1004,Zeynep,İzmir,Kozmetik,2,129.9 1005,Ali,Ankara,Gıda,6,45.5 """ df = pd.read_csv(io.StringIO(csv_metni)) print(df.loc[1, "musteri"]) # etiket 1 (varsayılan index) print(df.iloc[-1]) # son satır print(df.loc[df["sehir"] == "Ankara", ["musteri", "fiyat"]]) print(df[(df["kategori"] == "Gıda") & (df["adet"] >= 5)]) print(df[df["sehir"].isin(["İzmir", "Ankara"])]["siparis_id"].tolist())
loc[1] etiketi 1 olan satırı, iloc[1] ikinci satırı verir. Dilimde de fark var: loc[0:2] 2 dahil, iloc[0:2] hariç.Yeni sütun ve sıralama
Sütunlar arası aritmetik numpy gibi vektörleştirilmiştir. Yeni sütun atamak için df["tutar"] = ... ya da zincirleme kullanımda df.assign(tutar=...). Sıralama sort_values, en büyük n için nlargest.
import pandas as pd, io
csv_metni = """siparis_id,musteri,sehir,adet,fiyat
1001,Ayşe,İstanbul,3,45.5
1002,Mehmet,Ankara,1,899.0
1003,Ayşe,İstanbul,10,12.75
1004,Zeynep,İzmir,2,129.9
1005,Ali,Ankara,6,45.5
"""
df = pd.read_csv(io.StringIO(csv_metni))
df["tutar"] = df["adet"] * df["fiyat"]
df["buyuk"] = df["tutar"] > 200
print(df.sort_values("tutar", ascending=False).head(3))
print(df.sort_values(["sehir", "tutar"], ascending=[True, False])[["sehir", "tutar"]])
print(df.nlargest(2, "tutar")[["musteri", "tutar"]])
print(df.assign(kdv=lambda d: d["tutar"] * 0.2)[["tutar", "kdv"]].head(2))
df[maske]["tutar"] = 0 şeklinde zincirle atama yapma; asıl tabloyu değiştirmez. Her zaman df.loc[maske, "tutar"] = 0 yaz.Derinleş: kurulum, sürümler ve veriyi doğru okumak
Gerçek işte pandas'la geçen zamanın büyük kısmı hesap değil, veriyi doğru okumaktır. Türkiye'de Excel'den alınmış bir CSV'de ayraç noktalı virgüldür, ondalık virgüldür, binlik nokta; posta kodunun baştaki sıfırı kaybolur, tarih gün.ay.yıl yazılır, Türkçe karakterler bozulur. Bu bölümde kurulumdan başlayıp read_csv'nin bu sorunları çözen parametrelerini, ardından Excel, JSON ve SQL kaynaklarını görüyoruz.
Kurulum ve sürüm
# Sanal ortam etkinken (Windows, macOS, Linux aynı) python -m pip install pandas # numpy da birlikte gelir python -m pip install "pandas[excel]" # read_excel/to_excel için openpyxl vb. python -m pip install pyarrow # hızlı okuma, parquet, string türü python -c "import pandas as pd; print(pd.__version__)" # requirements.txt ile sabitleme pandas>=2.2,<4
Tarayıcıdaki motorda pandas 2.2, güncel bilgisayarlarda çoğunlukla 3.x bulunur. Bu dersteki kodlar iki sürümde de aynı sonucu verir. Hangi sürümle çalıştığını ve hangi yardımcı paketlerin kurulu olduğunu kodla da görebilirsin:
import pandas as pd
print(pd.__version__)
ana, kucuk = (int(x) for x in pd.__version__.split(".")[:2])
print("3.x mi?", ana >= 3)
# pd.show_versions() # tüm bağımlılıkların raporu (hata bildirirken eklenir)
| Konu | pandas 2.2 | pandas 3.x | İki sürümde güvenli yazım |
|---|---|---|---|
| Kopyalama | Copy-on-Write isteğe bağlı, zincirleme atama bazen çalışır | Copy-on-Write her zaman açık; zincirleme atama asıl tabloyu değiştirmez | df.loc[maske, "s"] = x |
| Metin sütunu türü | object | varsayılan str (string türü) | pd.api.types.is_string_dtype(s) ile kontrol et, == object yazma |
| Aylık sıklık | "M" uyarı verir | "M" kaldırıldı | "ME" (ay sonu), "MS" (ay başı) |
| JSON metni okuma | Ham metin uyarı verir | Ham metin hata verir | pd.read_json(io.StringIO(metin)) |
read_csv: Türkiye'deki CSV'yi okumak
Excel Türkçe bölge ayarıyla CSV kaydettiğinde ayraç ; olur, çünkü virgül ondalık işaretidir. Aşağıdaki parametreler en sık ihtiyaç duyacağın setir:
| Parametre | Ne yapar | Örnek |
|---|---|---|
sep | Sütun ayracı | sep=";", sekme için sep="\t" |
decimal | Ondalık işareti | decimal="," → "12,5" = 12.5 |
thousands | Binlik ayırıcı | thousands="." → "1.250" = 1250 |
encoding | Karakter kodlaması | "utf-8", "utf-8-sig", "cp1254" |
parse_dates + date_format | Tarih sütunlarını datetime yapar | parse_dates=["tarih"], date_format="%d.%m.%Y" |
dtype | Sütun türünü zorlar | dtype={"posta_kodu": str} baştaki sıfırı korur |
usecols | Yalnız gereken sütunlar | usecols=["musteri", "tutar"] |
na_values | Eksik sayılacak ek değerler | na_values=["-", "yok"] |
nrows, skiprows | İlk n satırı oku / baştaki satırları atla | skiprows=2 (rapor başlığı varsa) |
index_col | Bir sütunu index yapar | index_col="siparis_id" |
chunksize | Parça parça okur (büyük dosya) | for parca in pd.read_csv(f, chunksize=100_000) |
import io
import pandas as pd
excel_csv = """siparis_id;musteri_no;sehir;tutar;tarih
1001;00412;İstanbul;1.250,50;03.04.2024
1002;07730;Ankara;89,90;05.04.2024
1003;00412;İzmir;-;12.04.2024
"""
df = pd.read_csv(
io.StringIO(excel_csv),
sep=";", decimal=",", thousands=".",
dtype={"musteri_no": str}, # 00412 sıfırıyla kalsın
na_values=["-"], # "-" eksik değer
parse_dates=["tarih"], date_format="%d.%m.%Y",
)
print(df)
print(df["tutar"].sum()) # 1340.4
print(df["tarih"].dt.month.tolist()) # [4, 4, 4]
print(df["musteri_no"].tolist())
date_format="%d.%m.%Y" (ya da pd.to_datetime(..., format="%d.%m.%Y")) ver.Kirli başlık ve bozuk satırlar
ERP raporları çoğu zaman birkaç satırlık başlıkla ("Anadolu Market - Mart Raporu") başlar, sonunda "Toplam" satırı olur, arada da fazla ayraç içeren bozuk satırlar bulunur. skiprows baştaki satırları, skipfooter (yalnızca engine="python" ile) sondakileri atlar; on_bad_lines="skip" sütun sayısı uymayan satırları atlar, "warn" atlarken uyarı da yazar. Atlanan satırları sessizce kaybetmek yerine sayısını raporlamak iyi bir alışkanlıktır.
import io
import pandas as pd
rapor = """Anadolu Market - Mart Raporu
Oluşturma: 01.04.2024
id,sehir,tutar
1,Bursa,450
2,Ankara,120,FAZLA
3,İzmir,300
Toplam,,870
"""
df = pd.read_csv(io.StringIO(rapor), skiprows=2, skipfooter=1,
engine="python", on_bad_lines="skip", index_col="id")
print(df)
print(len(df), "satır okundu")
Okuduktan sonra mutlaka şu üç kontrolü yap: df.shape (satır sayısı beklenenle aynı mı?), df.dtypes (tutar sayı, tarih datetime mı?) ve df.isna().sum() (beklenmedik eksik var mı?). Büyük dosyalarda engine="pyarrow" (pyarrow kuruluysa) okumayı belirgin hızlandırır ama skipfooter gibi bazı parametreleri desteklemez.
Kodlama (encoding) sorunları
Eski sistemlerden ve Türkçe Windows'tan gelen dosyalar çoğunlukla cp1254 (Windows-1254) kodlamasındadır; UTF-8 sanılarak okunursa UnicodeDecodeError alırsın ya da "ÅŸ" gibi bozuk harfler görürsün. Excel'in Türkçe karakterleri doğru göstermesi için yazarken utf-8-sig (başında BOM işareti olan UTF-8) kullanılır.
import pandas as pd
with open("eski_sistem.csv", "w", encoding="cp1254") as f:
f.write("sehir;ciro\nŞanlıurfa;1200\nÇorum;800\n")
try:
pd.read_csv("eski_sistem.csv", sep=";") # utf-8 varsayılır
except UnicodeDecodeError as e:
print("Hata:", type(e).__name__)
df = pd.read_csv("eski_sistem.csv", sep=";", encoding="cp1254")
print(df["sehir"].tolist())
# Excel'de Türkçe açılsın: ; ayraç, virgül ondalık, BOM'lu UTF-8
df["ciro"] = df["ciro"] / 3
df.to_csv("rapor.csv", sep=";", decimal=",", encoding="utf-8-sig", index=False, float_format="%.2f")
print(open("rapor.csv", encoding="utf-8-sig").read())
Excel, JSON ve SQL kaynakları
read_excel için openpyxl paketi gerekir. Bu sayfada ilk kullanımda kendiliğinden iner (yaklaşık 270 KB); bilgisayarında şöyle kurarsın:
# python -m pip install openpyxl
import pandas as pd
df = pd.read_excel("satislar.xlsx", sheet_name="Mart", skiprows=2,
usecols="A:E", dtype={"musteri_no": str})
tum = pd.read_excel("satislar.xlsx", sheet_name=None) # {sayfa_adı: DataFrame}
df.to_excel("ozet.xlsx", index=False, sheet_name="Özet")
API'lerden gelen JSON çoğunlukla kayıt listesidir; iç içe alanlar için pd.json_normalize sütunları musteri.sehir gibi düzleştirir. Veritabanı için pd.read_sql(sorgu, baglanti, params=...) sorgu sonucunu doğrudan DataFrame yapar; kullanıcıdan gelen değeri sorguya f-string ile değil params ile ver (SQL enjeksiyonu).
import io, json, sqlite3
import pandas as pd
api = '[{"id": 1, "tutar": 450.0, "musteri": {"ad": "Ayşe", "sehir": "Bursa"}},' \
' {"id": 2, "tutar": 120.5, "musteri": {"ad": "Ali", "sehir": "Ankara"}}]'
print(pd.read_json(io.StringIO(api)).columns.tolist())
duz = pd.json_normalize(json.loads(api))
print(duz.columns.tolist())
con = sqlite3.connect(":memory:")
con.execute("CREATE TABLE siparis (id INTEGER, sehir TEXT, tutar REAL)")
con.executemany("INSERT INTO siparis VALUES (?, ?, ?)",
[(1, "Bursa", 450.0), (2, "Ankara", 120.5), (3, "Bursa", 80.0)])
q = "SELECT sehir, SUM(tutar) AS ciro FROM siparis WHERE tutar >= ? GROUP BY sehir"
print(pd.read_sql(q, con, params=(100,)))
con.close()
oku_siparis(yol) fonksiyonunda topla ve sonunda assert ile beklenen sütunları, türleri kontrol et. Kaynağın biçimi değiştiğinde hata raporu üretmeden önce yakalanır.df.info() çıktısında tutarın float, tarihin datetime olduğunu görmeden analize geçme.Derinleş: ders veri setleri (veri modülü)
Şimdiye kadar tabloları elle, küçük sözlüklerden kurduk. Gerçek işte ise veri hazır gelir: binlerce satırlık sipariş dökümü, on binlerce banka hareketi. Kesit Python bunun için tarayıcıda hazır bekleyen ortak veri setleri sunar. Banka, İK, teslimat, havayolu ve bisiklet setleri Kesit SQL ve Kesit Power BI ile satır satır aynıdır. Anadolu Market'te aynı şirket ve siparişler kullanılır, Power BI modeli zenginleştirilmiştir: anadolu_market SQL hâlidir, SQL ile yazdığın sorguyu burada pandas ile tekrar kurabilir, sonuçları karşılaştırabilirsin. Tüm veriler kurgusaldır; gerçek kişi ya da kuruma ait değildir.
Hangi setler var?
| Set | Konu | Öne çıkan tablolar |
|---|---|---|
anadolu_market | E-ticaret | customers (150), orders (910), order_items (1.620), products, ab_test, raw_signups |
ilgaz_bank | Bankacılık | bank_customers (500), bank_accounts (849), bank_txns (~42 bin) |
pusula_ik | İnsan kaynakları | çalışanlar, maaşlar, izinler, performans |
lezzet_kurye | Yemek teslimat | lk_orders, lk_couriers, lk_ratings |
kanat_havayollari | Havayolu | hv_flights, hv_delays, hv_bookings |
pedal_izmir | Bisiklet paylaşımı | bk_trips (~117 bin), bk_stations, bk_weather |
kesit_bi | Power BI yıldız şeması | Satislar, Urunler, Musteriler, Tarih, RFMSegmentleri |
kirli_veri | Temizlik pratiği | bayi, hedef, urun, musteri (dağınık Excel çıktıları) |
Listeyi ve bir setin tablolarını kodla görmek için:
import veri
veri.liste() # tüm setler, tablo ve satır sayıları
veri.tablolar("anadolu_market") # tablolar ve sütunları
print(veri.sutunlar("anadolu_market", "orders"))
"anadolu_" + "market") üretirsen dosya yüklenmez; adı her zaman açıkça yaz.Okumanın dört yolu
| Fonksiyon | Ne döndürür | Ne zaman |
|---|---|---|
veri.oku(set, tablo, **kw) | pandas DataFrame | Analizin çoğu. Ek argümanlar pd.read_csv'ye geçer: parse_dates, usecols, dtype. |
veri.kayitlar(set, tablo) | list[dict]; sayılar int/float, boşlar None | pandas gerekmeyen saf Python görevleri |
veri.baglan(set, tablolar=[...]) | Bellek içi sqlite3 bağlantısı | SQL yazmak, Kesit SQL sorgularını Python'dan çalıştırmak |
veri.yol(set, tablo) | CSV dosyasının yolu | pd.read_csv, csv modülü, polars ya da DuckDB ile kendin okumak |
import veri
import pandas as pd
siparis = veri.oku("anadolu_market", "orders", parse_dates=["order_date"])
print(siparis.shape) # (910, 5)
print(siparis["order_date"].min().date(), siparis["order_date"].max().date())
print(siparis["channel"].value_counts().to_dict())
CSV dosyalarında tarih metin olarak saklanır. parse_dates vermezsen order_date sütunu metin kalır ve .dt erişimcisi hata verir. Bu, gerçek veride en sık karşılaşacağın ilk hatadır.
import veri
hesaplar = veri.kayitlar("ilgaz_bank", "bank_accounts")
kapali = [h for h in hesaplar if h["closed_date"] is not None]
print(len(hesaplar), "hesap,", len(kapali), "kapalı")
print(hesaplar[0]["account_type"], hesaplar[0]["credit_limit"]) # vadesiz None
SQL ile pandas yan yana
veri.baglan seti bellekteki bir SQLite veritabanına yükler. Kesit SQL'de yazdığın sorgu aynen çalışır; sonucu pd.read_sql_query ile DataFrame'e alıp pandas'la devam edebilirsin. Büyük setlerde (pedal_izmir, ilgaz_bank) tablolar=[...] ile yalnız gereken tabloları yükle; hem bellek hem süre kazanırsın.
import veri
import pandas as pd
con = veri.baglan("anadolu_market", tablolar=["orders", "customers"])
sql = """
SELECT c.segment, COUNT(*) AS siparis
FROM orders o JOIN customers c ON c.customer_id = o.customer_id
WHERE o.status = 'delivered'
GROUP BY c.segment ORDER BY siparis DESC
"""
print(pd.read_sql_query(sql, con))
con.close()
veri modülü bu iki yolu taklit eder: baglan ambarı, oku ve yol dosyayı. Kendi bilgisayarında aynı kodu pd.read_csv("orders.csv") ya da sqlalchemy.create_engine(...) ile çalıştırırsın; analiz kısmı değişmez.Sonraki ileri veri derslerinde (temizlik, groupby, zaman serisi, görselleştirme, istatistik, kümeleme, NLP) bu setlerden gerçek tablolarla alıştırmalar bulacaksın.
Index kavramı
Index yalnızca satır numarası değildir; müşteri numarası ya da tarih olabilir (df.set_index("siparis_id")). O zaman loc gerçek iş anahtarıyla çalışır ve birleştirme/hizalama (alignment) otomatik olur: iki Series toplandığında aynı etiketler eşleşir, olmayanlar NaN olur. reset_index() geri döner.
Büyük veride
Milyonlarca satırda read_csv'ye usecols (gerekli sütunlar), dtype (küçük türler) ve chunksize (parça parça okuma) ver. df.memory_usage(deep=True) ile sütun başına bellek ölçülür; tekrar eden metinler için category türü çoğu zaman belleği onda birine indirir.
İş hayatında
Analist olarak ilk saatin hep aynıdır: yükle, info, describe, value_counts, birkaç maske. Bu "keşif" aşamasını küçük fonksiyonlara ayırırsan (yükle, filtrele, türet) hem test yazabilir hem de aynı raporu her ay tek komutla üretirsin.