Polars - 05 İfadeler ve Bağlamlar
Çrş 01 Temmuz 2026İfadeler (Expressions) ve Bağlamlar (Contexts)
Polars, veri dönüşümü için kendi Spesifik Etki Alanı Dilini (Domain Specific Language - DSL) geliştirmiştir. Bu dilin kullanımı çok kolaydır ve insan tarafından okunabilir kalan karmaşık sorgulara izin verir. Expression'lar (ifadeler) ve context'ler (bağlamlar), bu okunabilirliği sağlarken aynı zamanda Polars sorgu motorunun sorgularınızı olabildiğince hızlı çalıştırmak için optimize etmesine izin vermede çok önemlidir.
İfadeler
Polars'da bir ifade (expression), bir veri dönüşümünün lazy (tembel) bir temsilidir. ifadeler (expressions) modüler ve esnektir, yani bunları daha karmaşık ifadeler oluşturmak için yapı taşları olarak kullanabilirsiniz. İşte bir Polars ifade (expression) örneği:
import polars as pl
pl.col("weight") / (pl.col("height") ** 2)
Tahmin edebileceğiniz gibi, bu ifade "weight" adlı bir sütunu alır ve değerlerini "height" sütunundaki değerlerin karesine bölerek bir kişinin BMI'ını (vücut kütle endeksini) hesaplar.
Yukarıdaki kod, bir değişkende kaydedebileceğimiz, daha fazla manipüle edebileceğimiz veya yazdırabileceğimiz soyut bir hesaplamayı ifade eder:
bmi_expr = pl.col("weight") / (pl.col("height") ** 2)
print(bmi_expr)
[(col("weight")) / (col("height").pow([dyn int: 2]))]
İfadeler lazy olduğu için henüz hiçbir hesaplama gerçekleşmemiştir. İşte bu noktada bağlamlara (contexts) ihtiyacımız var.
Bağlamlar (Contexts)
Polars ifadelerinin bir sonuç üretmesi için yürütülecekleri bir bağlam'a ihtiyaçları vardır. Kullanıldığı bağlam'a bağlı olarak, aynı Polars ifade’si farklı sonuçlar üretebilir. Bu bölümde, Polars'ın sağladığı dört yaygın bağlam’ı (context'i) öğreneceğiz:
selectwith_columnsfiltergroup_by
Her bir bağlam'ın nasıl çalıştığını göstermek için aşağıdaki veri çerçevesi’ni (dataframe'i) kullanıyoruz.
Python
from datetime import date
df = pl.DataFrame(
{
"name": ["Alice Archer", "Ben Brown", "Chloe Cooper", "Daniel Donovan"],
"birthdate": [
date(1997, 1, 10),
date(1985, 2, 15),
date(1983, 3, 22),
date(1981, 4, 30),
],
"weight": [57.9, 72.5, 53.6, 83.1], # (kg)
"height": [1.56, 1.77, 1.65, 1.75], # (m)
}
)
print(df)
Rust
use chrono::prelude::*;
use polars::prelude::*;
let df: DataFrame = df!(
"name" => ["Alice Archer", "Ben Brown", "Chloe Cooper", "Daniel Donovan"],
"birthdate" => [
NaiveDate::from_ymd_opt(1997, 1, 10).unwrap(),
NaiveDate::from_ymd_opt(1985, 2, 15).unwrap(),
NaiveDate::from_ymd_opt(1983, 3, 22).unwrap(),
NaiveDate::from_ymd_opt(1981, 4, 30).unwrap(),
],
"weight" => [57.9, 72.5, 53.6, 83.1], // (kg)
"height" => [1.56, 1.77, 1.65, 1.75], // (m)
)
.unwrap();
println!("{df}");
shape: (4, 4)
┌────────────────┬────────────┬────────┬────────┐
│ name ┆ birthdate ┆ weight ┆ height │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ date ┆ f64 ┆ f64 │
╞════════════════╪════════════╪════════╪════════╡
│ Alice Archer ┆ 1997-01-10 ┆ 57.9 ┆ 1.56 │
│ Ben Brown ┆ 1985-02-15 ┆ 72.5 ┆ 1.77 │
│ Chloe Cooper ┆ 1983-03-22 ┆ 53.6 ┆ 1.65 │
│ Daniel Donovan ┆ 1981-04-30 ┆ 83.1 ┆ 1.75 │
└────────────────┴────────────┴────────┴────────┘
select Bağlamı
select bağlamı, ifadeleri sütunlar üzerinde uygular. select bağlamı, aggregasyonlar, diğer sütunların kombinasyonları veya literal'ler olan yeni sütunlar üretebilir:
Python
result = df.select(
bmi=bmi_expr,
avg_bmi=bmi_expr.mean(),
ideal_max_bmi=25,
)
print(result)
Rust
let bmi = col("weight") / col("height").pow(2);
let result = df
.clone()
.lazy()
.select([
bmi.clone().alias("bmi"),
bmi.clone().mean().alias("avg_bmi"),
lit(25).alias("ideal_max_bmi"),
])
.collect()?;
println!("{result}");
shape: (4, 3)
┌───────────┬───────────┬───────────────┐
│ bmi ┆ avg_bmi ┆ ideal_max_bmi │
│ --- ┆ --- ┆ --- │
│ f64 ┆ f64 ┆ i32 │
╞═══════════╪═══════════╪═══════════════╡
│ 23.791913 ┆ 23.438973 ┆ 25 │
│ 23.141498 ┆ 23.438973 ┆ 25 │
│ 19.687787 ┆ 23.438973 ┆ 25 │
│ 27.134694 ┆ 23.438973 ┆ 25 │
└───────────┴───────────┴───────────────┘
Bir select bağlam’ındaki ifade'ler, ya aynı uzunlukta series'ler üretmeli ya da bir skaler (scalar) üretmelidir. Skaler'ler, kalan series'lerin uzunluğuna uyacak şekilde broadcast edilir. Yukarıda kullanılan sayı gibi değişmez değerler (literal'ler) de yayınlanır.
Yayınlamanın ifade’ler içinde de gerçekleşebileceğini unutmayın. Örneğin, aşağıdaki ifade'yi ele alalım:
Python
result = df.select(deviation=(bmi_expr - bmi_expr.mean()) / bmi_expr.std())
print(result)
Rust
let result = df
.clone()
.lazy()
.select([((bmi.clone() - bmi.clone().mean()) / bmi.clone().std(1)).alias("deviation")])
.collect()?;
println!("{result}");
shape: (4, 1)
┌───────────┐
│ deviation │
│ --- │
│ f64 │
╞═══════════╡
│ 0.115645 │
│ -0.097471 │
│ -1.22912 │
│ 1.210946 │
└───────────┘
Hem çıkarma hem de bölme, ifade içinde yayınlama kullanır çünkü ortalamayı ve standart sapmayı hesaplayan alt ifade'ler tek değerlere (skaler) dönüşür.
select bağlam'ı çok esnek ve güçlüdür ve birbirinden bağımsız ve paralel olarak keyfi ifade'lari değerlendirmenize izin verir. Bu, daha sonra göreceğimiz diğer bağlam'lar için de geçerlidir.
with_columns Bağlamı
with_columns bağlam’ı select bağlam'ına çok benzer. İkisi arasındaki temel fark, with_columns bağlamı, orijinal dataframe'deki sütunları ve girdi ifade’lerine göre yeni sütunları içeren yeni bir dataframe oluşturmasıdır; oysa select bağlamı yalnızca girdi ifade'leri tarafından seçilen sütunları içerir:
Python
result = df.with_columns(
bmi=bmi_expr,
avg_bmi=bmi_expr.mean(),
ideal_max_bmi=25,
)
print(result)
Rust
let result = df
.clone()
.lazy()
.with_columns([
bmi.clone().alias("bmi"),
bmi.mean().alias("avg_bmi"),
lit(25).alias("ideal_max_bmi"),
])
.collect()?;
println!("{result}");
shape: (4, 7)
┌────────────────┬────────────┬────────┬────────┬───────────┬───────────┬───────────────┐
│ name ┆ birthdate ┆ weight ┆ height ┆ bmi ┆ avg_bmi ┆ ideal_max_bmi │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ date ┆ f64 ┆ f64 ┆ f64 ┆ f64 ┆ i32 │
╞════════════════╪════════════╪════════╪════════╪═══════════╪═══════════╪═══════════════╡
│ Alice Archer ┆ 1997-01-10 ┆ 57.9 ┆ 1.56 ┆ 23.791913 ┆ 23.438973 ┆ 25 │
│ Ben Brown ┆ 1985-02-15 ┆ 72.5 ┆ 1.77 ┆ 23.141498 ┆ 23.438973 ┆ 25 │
│ Chloe Cooper ┆ 1983-03-22 ┆ 53.6 ┆ 1.65 ┆ 19.687787 ┆ 23.438973 ┆ 25 │
│ Daniel Donovan ┆ 1981-04-30 ┆ 83.1 ┆ 1.75 ┆ 27.134694 ┆ 23.438973 ┆ 25 │
└────────────────┴────────────┴────────┴────────┴───────────┴───────────┴───────────────┘
select ve with_columns arasındaki bu fark nedeniyle, with_columns bağlamında kullanılan ifadeler, dataframe'deki orijinal sütunlarla aynı uzunlukta series'ler üretmelidir; oysa select bağlam'ındaki ifade'lerin kendi aralarında aynı uzunlukta series'ler üretmesi yeterlidir.
filter Bağlamı
filter bağlam'ı, Boolean (Doğru (True) / Yanlış (False)) veri türüne göre değerlendirilen bir veya daha fazla ifadeye dayalı olarak bir veri çerçevesinin satırlarını filtreler.
Python
result = df.filter(
pl.col("birthdate").is_between(date(1982, 12, 31), date(1996, 1, 1)),
pl.col("height") > 1.7,
)
print(result)
Rust
let result = df
.clone()
.lazy()
.filter(
col("birthdate")
.is_between(
lit(NaiveDate::from_ymd_opt(1982, 12, 31).unwrap()),
lit(NaiveDate::from_ymd_opt(1996, 1, 1).unwrap()),
ClosedInterval::Both,
)
.and(col("height").gt(lit(1.7))),
)
.collect()?;
println!("{result}");
shape: (1, 4)
┌───────────┬────────────┬────────┬────────┐
│ name ┆ birthdate ┆ weight ┆ height │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ date ┆ f64 ┆ f64 │
╞═══════════╪════════════╪════════╪════════╡
│ Ben Brown ┆ 1985-02-15 ┆ 72.5 ┆ 1.77 │
└───────────┴────────────┴────────┴────────┘
group_by Bağlamı ve Toplamalar/Kümelemeler (Aggregations)
group_by bağlam’ında, satırlar gruplandırma ifadelerinin benzersiz değerlerine göre gruplandırılır. Daha sonra ortaya çıkan gruplara değişken uzunluklarda olabilecek ifadeler uygulayabilirsiniz.
group_by bağlam'ını kullanırken, gruplamaları dinamik olarak hesaplamak için bir ifade kullanabilirsiniz:
Python
result = df.group_by(
(pl.col("birthdate").dt.year() // 10 * 10).alias("decade"),
).agg(pl.col("name"))
print(result)
Rust
let result = df
.clone()
.lazy()
.group_by([(col("birthdate").dt().year() / lit(10) * lit(10)).alias("decade")])
.agg([col("name")])
.collect()?;
println!("{result}");
shape: (2, 2)
┌────────┬─────────────────────────────────┐
│ decade ┆ name │
│ --- ┆ --- │
│ i32 ┆ list[str] │
╞════════╪═════════════════════════════════╡
│ 1990 ┆ ["Alice Archer"] │
│ 1980 ┆ ["Ben Brown", "Chloe Cooper", … │
└────────┴─────────────────────────────────┘
group_by kullandıktan sonra, gruplara toplama/kümeleme/yığınlama/birleştirme ifadelerini (aggregating expressions) uygulamak için agg kullanırız. Yukarıdaki örnekte yalnızca bir sütunun adını belirttiğimiz için, bu sütunun gruplarını listeler halinde alırız.
İstediğimiz kadar gruplama ifadesi (grouping expression) belirtebiliriz ve group_by bağlam’ı, belirtilen ifade’ler arasındaki benzersiz değerlere göre satırları gruplar. Burada, doğum onyılı ve kişinin 1.7 metreden kısa olup olmamasının bir kombinasyonuna göre gruplama yapıyoruz:
Python
result = df.group_by(
(pl.col("birthdate").dt.year() // 10 * 10).alias("decade"),
(pl.col("height") < 1.7).alias("short?"),
).agg(pl.col("name"))
print(result)
Rust
let result = df
.clone()
.lazy()
.group_by([
(col("birthdate").dt().year() / lit(10) * lit(10)).alias("decade"),
(col("height").lt(lit(1.7)).alias("short?")),
])
.agg([col("name")])
.collect()?;
println!("{result}");
shape: (3, 3)
┌────────┬────────┬─────────────────────────────────┐
│ decade ┆ short? ┆ name │
│ --- ┆ --- ┆ --- │
│ i32 ┆ bool ┆ list[str] │
╞════════╪════════╪═════════════════════════════════╡
│ 1980 ┆ true ┆ ["Chloe Cooper"] │
│ 1980 ┆ false ┆ ["Ben Brown", "Daniel Donovan"… │
│ 1990 ┆ true ┆ ["Alice Archer"] │
└────────┴────────┴─────────────────────────────────┘
Toplama/kümeleme/yığınlama/birleştirme ifadeleri (aggregating expressions) uygulandıktan sonra ortaya çıkan veri çerçevesi, soldaki her gruplandırma ifadesi için bir sütun ve ardından toplama/kümeleme/yığınlama/birleştirme ifadelerinin sonuçlarını temsil etmek için gereken sayıda sütun içerir. Buna karşılık istediğimiz kadar toplama ifadesi belirtebiliriz:
Python
result = df.group_by(
(pl.col("birthdate").dt.year() // 10 * 10).alias("decade"),
(pl.col("height") < 1.7).alias("short?"),
).agg(
pl.len(),
pl.col("height").max().alias("tallest"),
pl.col("weight", "height").mean().name.prefix("avg_"),
)
print(result)
Rust
let result = df
.clone()
.lazy()
.group_by([
(col("birthdate").dt().year() / lit(10) * lit(10)).alias("decade"),
(col("height").lt(lit(1.7)).alias("short?")),
])
.agg([
len(),
col("height").max().alias("tallest"),
cols(["weight", "height"])
.as_expr()
.mean()
.name()
.prefix("avg_"),
])
.collect()?;
println!("{result}");
shape: (3, 6)
┌────────┬────────┬─────┬─────────┬────────────┬────────────┐
│ decade ┆ short? ┆ len ┆ tallest ┆ avg_weight ┆ avg_height │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ i32 ┆ bool ┆ u32 ┆ f64 ┆ f64 ┆ f64 │
╞════════╪════════╪═════╪═════════╪════════════╪════════════╡
│ 1980 ┆ false ┆ 2 ┆ 1.77 ┆ 77.8 ┆ 1.76 │
│ 1980 ┆ true ┆ 1 ┆ 1.65 ┆ 53.6 ┆ 1.65 │
│ 1990 ┆ true ┆ 1 ┆ 1.56 ┆ 57.9 ┆ 1.56 │
└────────┴────────┴─────┴─────────┴────────────┴────────────┘
Diğer gruplama bağlam’ları için group_by_dynamic ve rolling bölümlerine de bakın.
İfade Genişletme (Expression Expansion)
Son örnek, iki gruplandırma ifadesi ve üç toplama ifadesi içeriyordu, ancak ortaya çıkan veri çerçevesi, beş yerine altı sütun içeriyordu. Yakından bakarsak, son toplama ifadesi iki farklı sütundan bahsediyordu: "weight" ve "height".
Polars ifade’leri, ifade genişletme (expression expansion) adı verilen bir özelliği destekler. İfade Genişletme, aynı dönüşümü birden fazla sütuna uygulamak istediğinizde kullanılan bir kısa gösterimdir. Gördüğümüz gibi,
pl.col("weight", "height").mean().name.prefix("avg_")
ifadesi "weight" ve "height" sütunlarının ortalama değerini hesaplar ve bunları sırasıyla "avg_weight" ve "avg_height" olarak yeniden adlandırır. Aslında, yukarıdaki ifade aşağıdaki iki ifade'yi kullanmaya eşdeğerdir:
[
pl.col("weight").mean().alias("avg_weight"),
pl.col("height").mean().alias("avg_height"),
]
Bu durumda, bu ifade, Polars'ın paralel olarak yürütebileceği iki bağımsız ifade'ye genişler. Diğer durumlarda, bir ifade'nin kaç tane bağımsız ifade’ye dönüşeceğini önceden bilemeyebiliriz.
Şu basit ama açıklayıcı örneği ele alalım:
(pl.col(pl.Float64) * 1.1).name.suffix("*1.1")
Bu ifade, Float64 veri tipine sahip tüm sütunları 1.1 ile çarpar. Bunun uygulandığı sütun sayısı, her veri çerçevesinin şema (schema)'sına bağlıdır. Kullandığımız veri çerçevesi durumunda, iki sütuna uygulanır:
Python
expr = (pl.col(pl.Float64) * 1.1).name.suffix("*1.1")
result = df.select(expr)
print(result)
Rust
let expr = (dtype_col(&DataType::Float64).as_selector().as_expr() * lit(1.1))
.name()
.suffix("*1.1");
let result = df.lazy().select([expr.clone()]).collect()?;
println!("{result}");
shape: (4, 2)
┌────────────┬────────────┐
│ weight*1.1 ┆ height*1.1 │
│ --- ┆ --- │
│ f64 ┆ f64 │
╞════════════╪════════════╡
│ 63.69 ┆ 1.716 │
│ 79.75 ┆ 1.947 │
│ 58.96 ┆ 1.815 │
│ 91.41 ┆ 1.925 │
└────────────┴────────────┘
Aşağıdaki df2 veri çerçevesi'nde ise, aynı ifade hiçbir sütuna uygulanmaz çünkü Float64 veri tipinde hiçbir sütun yoktur:
Python
df2 = pl.DataFrame(
{
"ints": [1, 2, 3, 4],
"letters": ["A", "B", "C", "D"],
}
)
result = df2.select(expr)
print(result)
Rust
let df2: DataFrame = df!(
"ints" => [1, 2, 3, 4],
"letters" => ["A", "B", "C", "D"],
)
.unwrap();
let result = df2.lazy().select([expr]).collect()?;
println!("{result}");
shape: (0, 0)
┌┐
╞╡
└┘
Aynı ifade'nin düzinelerce sütuna genişleyeceği bir senaryo hayal etmek de aynı derecede kolaydır.
Bir sonraki bölümde, bir ifade'nin belirli bir şema (schema) karşısında nasıl genişleyeceğini önizlemek için kullanabileceğiniz lazy API ve explain fonksiyonu hakkında bilgi edineceksiniz.
Sonuç
İfade’ler (Expressions) lazy olduğu için, bir ifade'yi bir bağlam içinde kullandığınızda Polars, temsil ettiği veri dönüşümünü çalıştırmadan önce ifade’nizi basitleştirmeyi deneyebilir. Bir bağlam içindeki ayrı ifade'ler utandırıcı derecede paraleldir (embarrassingly parallel) ve Polars bundan yararlanırken, ifade genişletmeyi (expression expansion) kullanıldığında ifade yürütmeyi de paralelleştirir. Daha fazla performans kazancı, bir sonraki bölümde tanıtılan Polars lazy API kullanılarak elde edilebilir.
İfade’lerin yeteneklerinin yalnızca yüzeyine dokunduk. Çok daha fazla ifade vardır ve bunlar çeşitli şekillerde birleştirilebilir. Mevcut farklı ifade türleri hakkında daha derinlemesine bilgi için expression'lar bölümüne bakın.
- Ek Liste ve SQL bağlam'ları vardır, bunlar bu kılavuzun ilerleyen bölümlerinde ele alınmaktadır. Ancak basitlik adına, şimdilik bunları kapsam dışı bırakıyoruz.