Kurs
Pek çok kişi “döngülerden kaçınmak” için .apply() yöntemine başvurur ve bunun hızlı ve kolay olmasını bekler. Pratikte, satır bazlı .apply(axis=1) hâlâ Python seviyesinde bir döngüdür. Büyük veride yavaş olabilir ve bazen beklemediğiniz şekiller döndürebilir. Çözüm basit: yaygın görevler için vektörleştirilmiş pandas/NumPy işlemlerini kullanın; .apply() yöntemini ise gerçekten birden çok sütuna ihtiyaç duyan mantık için saklayın.
Bu rehber, .apply() yönteminin bugün nasıl çalıştığını gösterir, yaygın tuzakları vurgular ve daha hızlı ile daha anlaşılır doğrudan kullanılabilir kalıplar sunar.
DataFrame.apply() ve Series.apply() Nedir?
DataFrame.apply(func, axis=0) varsayılan olarak her sütunda func çağrısı yapar. axis=1 ile her satırda func çağrılır. Series.apply(func) her bir öğede (veya son sürümlerdeki imzalara bağlı olarak tüm Series üzerinde) func çağrısı yapar.
- Satır bazlı vs. sütun bazlı:
axis=1“her satır” anlamına gelir.axis=0(varsayılan) “her sütun” anlamına gelir.
- Dönüş şekli kuralları (pandas ≥0.23): Fonksiyonunuz satır/sütun başına bir skaler döndürürse bir
Serieselde edersiniz. BirSeriesveyadictdöndürürse, pandas bu anahtarları birDataFrameiçine açar. Bir liste/dizi döndürürse,result_type='expand'ayarlamadığınız sürece liste Serisi elde edersiniz.
- Daha sıkı hatalar (pandas ≥2.0): Liste benzeri uzunluk uyumsuzlukları artık sessizce tutarsız sonuçlar üretmek yerine hata yükseltiyor.
Önerilen İlk Tercih: Vektörleştirilmiş İşlemler
Sütunları birleştirmeyi veya dönüştürmeyi içeren işlerin çoğu vektörleştirilmiş ifadeler veya yerleşik yöntemlerle daha hızlı ve daha anlaşılırdır. Aşağıdaki örnek, bir beyzbol takımının sayı farkını .apply() olmadan hesaplar.
import pandas as pd
team_stats = pd.DataFrame({
"Team": ["ARI", "ATL", "BAL"],
"League": ["NL", "NL", "AL"],
"Year": [2012, 2012, 2012],
"RunsScored": [734, 700, 712],
"RunsAllowed": [688, 600, 705],
"Wins": [81, 94, 93],
"Games": [162, 162, 162],
"Playoffs": [0, 1, 1],
})
# Vectorized: fast and idiomatic
team_stats["RunDiff"] = team_stats["RunsScored"] - team_stats["RunsAllowed"]
print(team_stats[["Team", "RunsScored", "RunsAllowed", "RunDiff"]])
Gerçekten Gerekli Olduğunda Satır Bazlı .apply()
Mantığınız gerçekten birden çok sütuna yayılıyor ve kolayca vektörleştirilemiyorsa (örneğin, birkaç alana bağlı koşullu kurallar), .apply(axis=1) kullanın.
birden çok sütundan türetilmiş bir değer hesaplayın
Bu kalıp, birden fazla girdiyi kullanarak satır başına bir değer hesaplar. Yukarıdaki vektörleştirilmiş yaklaşım hâlâ tercih edilir, ancak burada doğru satır bazlı kullanımı ve hızı ile çıktıyı etkileyen seçenekleri gösteriyoruz.
def compute_run_diff(row):
# Treat the row as read-only; return a scalar
return row["RunsScored"] - row["RunsAllowed"]
# Row-wise apply (Python-level loop; can be slow on large data)
team_stats["RunDiff_apply"] = team_stats.apply(compute_run_diff, axis=1)
Fonksiyon sayısalsa ve yalnızca ham dizilere ihtiyaç duyuyorsa, raw=True geçmek, fonksiyonunuza bir NumPy dizisi sağlayarak bazı pandas yükünü atlar.
import numpy as np
def compute_run_diff_raw(values):
# values is a NumPy array when raw=True
# Order matches the column order we select
rs, ra = values
return rs - ra
team_stats["RunDiff_raw"] = team_stats[["RunsScored", "RunsAllowed"]].apply(
compute_run_diff_raw, axis=1, raw=True
)
fonksiyon girdisini bir kez doğrulayın
Yeni bir satır fonksiyonunu bağlarken, iletilenin ne olduğunu doğrulamak için bir satırı bir kez yazdırırım.
def debug_row(row):
# Print the first row only
if row.name == team_stats.index[0]:
print("Example row:", row.to_dict())
return 0
_ = team_stats.apply(debug_row, axis=1)
Vaka Çalışması: Toplamlar, Sezon Toplamları ve Metin Bayrakları
Yıllara göre Rays istatistikleri verildiğinde, mümkün olduğunda yerleşik yöntemleri ve öğe bazlı araçları .apply() yerine tercih edin.
rays_by_year = pd.DataFrame(
{
"Year": [2012, 2011, 2010, 2009, 2008],
"RunsScored": [697, 707, 802, 803, 774],
"RunsAllowed": [577, 614, 649, 754, 671],
"Wins": [90, 91, 96, 84, 97],
"Playoffs": [0, 1, 1, 0, 1],
}
).set_index("Year")
sütun toplamlarını verimli şekilde alın
.apply(sum) yerine DataFrame.sum() kullanın. Daha hızlı ve daha anlaşılır.
# Preferred
totals = rays_by_year.sum(axis=0)
print(totals)
# If you must use apply (not recommended here)
totals_apply = rays_by_year.apply(sum, axis=0)
bir sezonda atılan toplam sayıyı hesaplayın
Sütunlar arasında vektörleştirilmiş aritmetik kullanın; .apply() gerekmez.
rays_by_year["TotalRuns"] = rays_by_year["RunsScored"] + rays_by_year["RunsAllowed"]
print(rays_by_year[["RunsScored", "RunsAllowed", "TotalRuns"]].head())
0/1 bayrağını metne dönüştürün
Tek bir sütunda öğe bazlı dönüşümler için Series.map() veya replace() tercih edin.
# Using map with a dict
rays_by_year["PlayoffsText"] = rays_by_year["Playoffs"].map({0: "No", 1: "Yes"})
# Equivalent with replace
rays_by_year["PlayoffsText2"] = rays_by_year["Playoffs"].replace({0: "No", 1: "Yes"})
.apply() Çıktısının Şeklini Kontrol Edin
Fonksiyonunuz satır başına birden fazla değer döndürdüğünde, şekli açıkça belirtin. Bu, sürprizleri ve gelecekteki sürüm kırılmalarını önler.
bir liste döndürün ve sütunlara genişletin
Bir liste/diziyi birden fazla sütuna bölmek için result_type='expand' kullanın.
def wins_losses(row):
# Derive wins and losses as two outputs
wins = row["Wins"]
losses = row["Games"] - row["Wins"] if "Games" in row else np.nan
return [wins, losses]
# Example using team_stats, which has "Wins" and "Games"
expanded = team_stats.apply(wins_losses, axis=1, result_type="expand")
expanded.columns = ["WinsOut", "LossesOut"]
team_stats = pd.concat([team_stats, expanded], axis=1)
sütunları otomatik adlandırmak için bir series veya dict döndürün
Her satırdan bir Series veya dict döndürmek, bu etiketlerle eşleşen sütun adlarına sahip bir DataFrame üretir.
def summary_row(row):
return pd.Series(
{
"IsWinningSeason": row["Wins"] >= 90,
"RunRatio": (row["RunsScored"] / row["RunsAllowed"]),
}
)
summary = team_stats.apply(summary_row, axis=1)
team_stats = pd.concat([team_stats, summary], axis=1)
Gerçekten Önemli Performans İpuçları
Satır bazlı .apply(axis=1) kullanışlıdır ancak büyük çerçevelerde yavaştır; çünkü Python fonksiyonunuzu satır başına bir kez çağırır. Bu kalıplar o darboğazdan kaçınır.
- Vektörleştirilmiş pandas/NumPy işlemlerini ve
sum,mean,clip,where,astypeve string/erişimci yöntemleri gibi yerleşik yöntemleri tercih edin.
- Dizi kabul eden sayısal satır/sütun işlemleri için, pandas yükünü azaltmak ve bir NumPy dizisi almak üzere
raw=Truegeçin.
- Tek bir sütunda öğe bazlı işler için,
Series.map()veya vektörleştirilmiş yöntemleri,DataFrame.apply(axis=1)yerine kullanın.
- Fonksiyonunuz içinde sağlanan satır/sütunu değiştirmekten kaçının; bunun yerine yeni bir değer döndürün.
Bilmeniz Gereken Sürüm Notları (pandas 2.x)
Son pandas sürümleri, sonuçların daha öngörülebilir olması için .apply() etrafındaki davranışı sıkılaştırdı.
- Liste benzeri dönüşler: 2.0+ sürümlerinde, uyumsuz liste uzunlukları hata yükseltir. Tutarlı uzunluklar kullanın ve birden çok sütun istediğinizde
result_type='expand'ayarlayın.
- Çıktı genişletme: Bir
Seriesveyadictdöndürmek, anahtarları sütunlar olan bir DataFrame'e genişler (0.23'ten beri stabil).
Series.apply()değişiklikleri:convert_dtypebağımsız değişkeni kullanım dışı. Karışık türlere ihtiyaç duyuyorsanız, önceobjecttürüne dönüştürün (örneğin,s.astype("object").apply(fn)). Daha yeni imzalar, daha fazla bağlamdafnfonksiyonunun skalerler mi yoksa bir Series mi alacağını kontrol etmeye izin verir (örneğin,by_row).
.apply() vs. .map() vs. .applymap() vs. .agg()
Dönüşümünüzün şekline uygun API'yi seçin.
Series.map(func_or_dict): Tek bir sütunda öğe bazlı dönüşüm. En iyi sözlük eşlemeleri veya basit fonksiyonlar için.
DataFrame.apply(func, axis=1): Birden çok sütun gerektiren satır bazlı mantık.
DataFrame.apply(func, axis=0): Sütun bazlı mantık (her girdi bir sütunu temsil eden bir Series'tir).
DataFrame.applymap(func): Her hücrede öğe bazlı. Seyrek kullanın; vektörleştirilmiş yöntemler daha hızlıdır.
.agg()/.transform(): Toplamalar ve grup bazlı dönüşümler; groupby ardışık düzenlerinde bunları tercih edin.
Yaygın Hatalar ve Hızlı Çözümler
Yanlış sonuçlara veya yavaşlamalara en sık neden olan sorunlar ve bunları düzeltme yolları şunlardır.
- Satır başına mantıkta axis=1'i unutmak: Fonksiyonunuz birden bire satırlar yerine sütunlar alıyorsa,
axis=1ekleyin.
- Beklenmedik liste Serisi: Her satırdan bir liste/dizi döndürürken, sütunlara bölmek için
result_type='expand'ayarlayın.
- Yavaş satır bazlı kod: Vektörleştirilmiş ifadeler veya yerleşiklerle değiştirin; mümkün değilse, yükü azaltmak için
raw=Truedüşünün.
- Girdi satırını değiştirme: Girdileri salt-okunur olarak ele alın ve yeni değerler döndürün.
- Dtype kayması: Fonksiyonunuz bazen tam sayı olmayan değerler döndürürse, tamsayı sütunları float veya object'e yükseltilebilir. Gerekirse sonradan
astypeile dönüştürün.
Sonuç
.apply() esnek bir araçtır, ancak bir performans kısayolu değildir. Aritmetik, toplama ve tek bir sütunda öğe bazlı dönüşümler için vektörleştirilmiş işlemleri kullanın. DataFrame.apply(axis=1) yöntemine, mantığınız gerçekten satır başına birden çok sütun gerektirdiğinde başvurun. Kullandığınızda, çıktının şeklini result_type ile kontrol edin, sayısal fonksiyonlar için raw=True seçeneğini değerlendirin ve dtypes'ı gözlemleyin. Bu kalıplar, modern pandas'ta öngörülebilir sonuçlar üretir ve veriniz büyüdükçe daha iyi ölçeklenir.
