flowchart LR
A["Ham CSV"] --> B["Oku"]
B --> C["Türleri dönüştür"]
C --> D["Doğrula"]
D --> E["Filtrele / grupla"]
E --> F["Ara / sırala"]
F --> G["Özet / rapor"]
Veri işleme uygulaması
Şimdiye kadar koleksiyonları, arama yöntemlerini, sıralamayı ve maliyet sezgisini ayrı ayrı inceledik. Bu hafta bunları tek bir veri işleme akışında birleştireceğiz.
Örnek bağlamımız bir CSV dosyasından gelen ürün kayıtları olacak. Web sürümünde tarayıcı içinde çalışabilmek için CSV metnini StringIO ile dosya benzeri bir kaynak olarak kullanacağız. Aynı kod gerçek bir dosyada open() ile de uygulanabilir.
1 Bu hafta neleri yapabilmelisiniz?
Bölümün sonunda:
- CSV/metin verisini kayıtlara dönüştürebilmeli,
- sayısal alanlarda tür dönüşümü yapabilmeli,
- veriyi filtreleyebilmeli, gruplayabilmeli ve sıralayabilmeli,
- kayıtları anahtara göre indeksleyebilmeli,
- arama ve özetleme işlemlerini fonksiyonlara ayırabilmeli,
- hatalı veya eksik veri durumlarını fark edebilmeli,
- bir veri işleme görevi için uygun koleksiyonları gerekçelendirebilmelisiniz.
2 Örnek veri
Elimizde şu CSV olduğunu düşünelim:
code,name,category,price,stock
K101,Kalem,Kırtasiye,12.5,40
D205,Defter,Kırtasiye,45.0,8
M310,Mouse,Elektronik,325.0,12
K420,Klavye,Elektronik,590.0,5
S115,Silgi,Kırtasiye,8.0,55
Her satır bir kaydı, ilk satır ise alan adlarını gösterir.
3 CSV metnini okumak
#| edit: false
#| completion: false
import csv
from io import StringIO
csv_text = """code,name,category,price,stock
K101,Kalem,Kırtasiye,12.5,40
D205,Defter,Kırtasiye,45.0,8
M310,Mouse,Elektronik,325.0,12
K420,Klavye,Elektronik,590.0,5
S115,Silgi,Kırtasiye,8.0,55
"""
reader = csv.DictReader(StringIO(csv_text))
records = list(reader)
print(records[0])
print(records[0]["price"], type(records[0]["price"]).__name__)
csv.DictReader, her satırı sözlük olarak üretir. Ancak CSV’den gelen alanlar başlangıçta string’dir. "12.5" sayısal karşılaştırma için önce float yapılmalıdır.
4 Türleri dönüştürmek
#| edit: false
#| completion: false
import csv
from io import StringIO
csv_text = """code,name,category,price,stock
K101,Kalem,Kırtasiye,12.5,40
D205,Defter,Kırtasiye,45.0,8
M310,Mouse,Elektronik,325.0,12
K420,Klavye,Elektronik,590.0,5
S115,Silgi,Kırtasiye,8.0,55
"""
records = []
for row in csv.DictReader(StringIO(csv_text)):
row["price"] = float(row["price"])
row["stock"] = int(row["stock"])
records.append(row)
print(records[0])
print(type(records[0]["price"]).__name__)
print(type(records[0]["stock"]).__name__)
Tür dönüşümünü erken yapmak, sonraki adımları sadeleştirir.
5 Gerçek dosyada aynı fikir
Gerçek bir products.csv dosyası için temel kalıp şöyledir:
import csv
with open("products.csv", encoding="utf-8", newline="") as file:
reader = csv.DictReader(file)
for row in reader:
print(row)Web kitabındaki örneklerde dosya dağıtımıyla uğraşmamak için StringIO kullanıyoruz; veri işleme mantığı aynıdır.
6 İşleme hattı
Bir veri işleme görevini şu aşamalarla düşünmek yararlıdır:
Her aşamanın girdisi ve çıktısı belirgin olduğunda hata ayıklamak kolaylaşır.
7 Filtreleme
Stoku 10’un altında olan ürünleri bulalım:
#| edit: false
#| completion: false
records = [
{"code": "K101", "name": "Kalem", "category": "Kırtasiye", "price": 12.5, "stock": 40},
{"code": "D205", "name": "Defter", "category": "Kırtasiye", "price": 45.0, "stock": 8},
{"code": "M310", "name": "Mouse", "category": "Elektronik", "price": 325.0, "stock": 12},
{"code": "K420", "name": "Klavye", "category": "Elektronik", "price": 590.0, "stock": 5},
]
low_stock = []
for record in records:
if record["stock"] < 10:
low_stock.append(record)
for record in low_stock:
print(record["name"], record["stock"])
8 Gruplama
Ürünleri kategoriye göre gruplayalım:
#| edit: false
#| completion: false
records = [
{"name": "Kalem", "category": "Kırtasiye", "price": 12.5},
{"name": "Defter", "category": "Kırtasiye", "price": 45.0},
{"name": "Mouse", "category": "Elektronik", "price": 325.0},
{"name": "Klavye", "category": "Elektronik", "price": 590.0},
]
groups = {}
for record in records:
category = record["category"]
if category not in groups:
groups[category] = []
groups[category].append(record)
for category, products in groups.items():
print(category, "->", len(products), "ürün")
Bu, 3. ve 4. haftalarda gördüğümüz sözlük + liste kalıbıdır.
9 Kategori toplamı
Bu kez kategori başına ürün değerini toplayalım:
#| edit: false
#| completion: false
records = [
{"name": "Kalem", "category": "Kırtasiye", "price": 12.5, "stock": 40},
{"name": "Defter", "category": "Kırtasiye", "price": 45.0, "stock": 8},
{"name": "Mouse", "category": "Elektronik", "price": 325.0, "stock": 12},
{"name": "Klavye", "category": "Elektronik", "price": 590.0, "stock": 5},
]
category_values = {}
for record in records:
category = record["category"]
value = record["price"] * record["stock"]
category_values[category] = category_values.get(category, 0) + value
for category, value in category_values.items():
print(category, round(value, 2))
Burada dict değerlerini liste değil, toplam tutan sayılar olarak kullandık.
10 Koda göre indeks oluşturmak
Aynı veri üzerinde ürün koduna göre sık sık arama yapacaksak kayıtları her defasında doğrusal aramak yerine bir indeks oluşturabiliriz:
#| edit: false
#| completion: false
records = [
{"code": "K101", "name": "Kalem", "stock": 40},
{"code": "D205", "name": "Defter", "stock": 8},
{"code": "M310", "name": "Mouse", "stock": 12},
]
by_code = {}
for record in records:
by_code[record["code"]] = record
print(by_code["M310"])
Bu noktada verinin iki görünümü vardır:
records: sırayı koruyan kayıt listesi,by_code: koda göre hızlı erişim için sözlük.
Aynı kayıt sözlüklerinin iki yapı tarafından referans edildiğine dikkat edin.
11 Sıralama
Ürünleri önce kategoriye, sonra aynı kategori içinde fiyata göre sıralayalım:
#| edit: false
#| completion: false
records = [
{"name": "Kalem", "category": "Kırtasiye", "price": 12.5},
{"name": "Defter", "category": "Kırtasiye", "price": 45.0},
{"name": "Mouse", "category": "Elektronik", "price": 325.0},
{"name": "Klavye", "category": "Elektronik", "price": 590.0},
]
ordered = sorted(
records,
key=lambda record: (record["category"], record["price"]),
)
for record in ordered:
print(record["category"], record["price"], record["name"])
12 Fonksiyonlara ayırma
Veri işleme kodunun tamamını tek blokta tutmak yerine adımları fonksiyonlara ayırabiliriz:
#| edit: false
#| completion: false
import csv
from io import StringIO
def load_products(csv_text):
products = []
for row in csv.DictReader(StringIO(csv_text)):
row["price"] = float(row["price"])
row["stock"] = int(row["stock"])
products.append(row)
return products
def low_stock_products(products, limit=10):
result = []
for product in products:
if product["stock"] < limit:
result.append(product)
return result
def index_by_code(products):
result = {}
for product in products:
result[product["code"]] = product
return result
csv_text = """code,name,category,price,stock
K101,Kalem,Kırtasiye,12.5,40
D205,Defter,Kırtasiye,45.0,8
M310,Mouse,Elektronik,325.0,12
K420,Klavye,Elektronik,590.0,5
"""
products = load_products(csv_text)
print(low_stock_products(products))
print(index_by_code(products)["K101"])
Her fonksiyonun tek bir göreve odaklanması test etmeyi kolaylaştırır.
13 Alıştırma — kategoriye göre özet
Aşağıdaki fonksiyon her kategori için toplam stok miktarını döndüren bir sözlük üretmelidir.
#| exercise: hafta12-category-stock
#| completion: false
#| persist: true
def stock_by_category(products):
totals = {}
for product in products:
# Burayı tamamlayın.
pass
return totals
products = [
{"name": "Kalem", "category": "Kırtasiye", "stock": 40},
{"name": "Defter", "category": "Kırtasiye", "stock": 8},
{"name": "Mouse", "category": "Elektronik", "stock": 12},
{"name": "Klavye", "category": "Elektronik", "stock": 5},
]
print(stock_by_category(products))
Beklenen sonuç:
{'Kırtasiye': 48, 'Elektronik': 17}
#| exercise: hafta12-category-stock
#| check: true
scope = {}
try:
exec(user_code, scope)
fn = scope.get("stock_by_category")
sample = [
{"name": "A", "category": "X", "stock": 2},
{"name": "B", "category": "Y", "stock": 3},
{"name": "C", "category": "X", "stock": 5},
]
correct = callable(fn) and fn(sample) == {"X": 7, "Y": 3} and fn([]) == {}
except Exception:
correct = False
feedback = (
{"correct": True, "message": "Kategori stok toplamları doğru hesaplandı."}
if correct
else {"correct": False, "message": "category değerini anahtar olarak kullanın ve totals.get(category, 0) üzerine stock ekleyin."}
)
feedback
totals[category] = totals.get(category, 0) + product["stock"] kalıbını deneyin.
Solution.
def stock_by_category(products):
totals = {}
for product in products:
category = product["category"]
totals[category] = totals.get(category, 0) + product["stock"]
return totals14 Hatalı veriyle ne yapacağız?
Gerçek CSV her zaman temiz olmayabilir:
code,name,category,price,stock
K101,Kalem,Kırtasiye,12.5,40
D205,Defter,Kırtasiye,abc,8
M310,Mouse,Elektronik,325.0,
float("abc") ve int("") hata üretir. Hatanın nerede yönetileceği uygulamanın gereksinimine bağlıdır. Basit bir yaklaşım hatalı satırı raporlayıp atlamak olabilir:
#| edit: false
#| completion: false
import csv
from io import StringIO
csv_text = """code,name,category,price,stock
K101,Kalem,Kırtasiye,12.5,40
D205,Defter,Kırtasiye,abc,8
M310,Mouse,Elektronik,325.0,
"""
valid = []
errors = []
for line_no, row in enumerate(csv.DictReader(StringIO(csv_text)), start=2):
try:
row["price"] = float(row["price"])
row["stock"] = int(row["stock"])
valid.append(row)
except ValueError:
errors.append(line_no)
print("Geçerli:", valid)
print("Hatalı satırlar:", errors)
Burada hatayı sessizce yok etmek yerine hangi satırların sorunlu olduğunu kaydediyoruz.
15 Uçtan uca küçük rapor
Bir veri işleme görevinde artık şu zinciri kurabiliyoruz:
- CSV’yi oku,
- türleri dönüştür,
- hatalı satırları ayır,
- düşük stokları filtrele,
- kategori toplamlarını hesapla,
- ürünleri ölçüte göre sırala,
- koda göre indeks oluştur.
Bu adımların her biri daha önce öğrendiğiniz bir veri yapısı veya algoritma kararına dayanır.
16 Hangi yapıyı neden kullandık?
| İhtiyaç | Yapı |
|---|---|
| kayıtların sırasını korumak | list |
| bir kaydın alanları | dict |
| ürün kodundan kayda erişmek | dict |
| kategori başına toplam | dict |
| benzersiz kategori adları gerekirse | set |
| sıralı rapor | sorted() sonucu list |
17 Bölüm özeti
- CSV verisi
csv.DictReaderile sözlük kayıtlarına dönüştürülebilir. - Sayısal alanların string olarak gelmesi nedeniyle tür dönüşümü gerekir.
- Filtreleme, gruplama, indeksleme, sıralama ve özetleme aynı veri akışında birlikte kullanılabilir.
- Fonksiyonlara ayırma veri işleme kodunu test edilebilir ve okunabilir kılar.
- Hatalı veri durumları açıkça ele alınmalıdır.
- Koleksiyon seçimi, verinin ne olduğundan çok veri üzerinde hangi işlemlerin yapılacağına bağlıdır.
18 Kendinizi kontrol edin
DictReaderile okunanpricealanı neden hemen sayı olmayabilir?- Ürün koduna göre çok sık erişim için neden ayrı bir sözlük indeksi yararlı olabilir?
- Filtreleme ile gruplama arasındaki fark nedir?
- Hatalı CSV satırını sessizce atmak neden sorun olabilir?
- Aynı kaydı hem liste hem sözlük indeksinden görmek referans açısından ne anlama gelir?