Rust Data Pipelines: From Files to Clean Databases and Web Dashboards
Introduction
We are building a small environmental data pipeline. Raw water-quality monitoring files arrive as CSV. Our Rust tool validates them, cleans bad records, fills safe gaps, stores trusted measurements, and powers a dashboard.
Data Pipeline
About the dataset used
The dataset1 contains raw water-quality monitoring data from Cork Harbour, Moy Killala, and 15 other coastal locations in Ireland. The raw extracted dataset has over 1.27 million entries, and the repository also includes a transformed/pivoted version with 29,159 rows across 11 water-quality parameters. The files are CSV, so they are easy to use for the โfiles โ clean database โ dashboardโ flow.
Tools and libraries
We use Rust2 to implement our Data Pipeline by leveraging Polars3.
DataFrame
//! ```cargo
//! [dependencies]
//! chrono = "0.4.45"
//! polars = { version = "0.54.4", features = ["lazy", "temporal", "sql"] }
//! ```
use chrono::NaiveDate;
use polars::{
df,
error::PolarsError,
frame::DataFrame,
prelude::{IntoLazy, col},
};
fn main() -> Result<(), PolarsError> {
let mut 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(1997, 3, 22).unwrap(),
NaiveDate::from_ymd_opt(1997, 4, 30).unwrap(),
],
"weight" => [57.9, 72.5, 54.6, 83.1], // (kg)
"height" => [1.56, 1.77, 1.65, 1.75], // (m)
)
.unwrap();
println!("Data:");
print!("{df}\n");
let head = df.head(Some(2));
println!("Head:");
print!("{head}\n");
Ok(())
}Data: 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 โ 1997-03-22 โ 54.6 โ 1.65 โ โ Daniel Donovan โ 1997-04-30 โ 83.1 โ 1.75 โ โโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโดโโโโโโโโโ Head: shape: (2, 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 โ โโโโโโโโโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโดโโโโโโโโโ
Selecting columns
//! ```cargo
//! [dependencies]
//! chrono = "0.4.45"
//! polars = { version = "0.54.4", features = ["lazy", "temporal", "sql"] }
//! ```
use chrono::NaiveDate;
use polars::{
df,
error::PolarsError,
frame::DataFrame,
prelude::{IntoLazy, col},
};
fn main() -> Result<(), PolarsError> {
let mut 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(1997, 3, 22).unwrap(),
NaiveDate::from_ymd_opt(1997, 4, 30).unwrap(),
],
"weight" => [57.9, 72.5, 54.6, 83.1], // (kg)
"height" => [1.56, 1.77, 1.65, 1.75], // (m)
)
.unwrap();
let result = df
.clone()
.lazy()
.select([
col("name"),
col("birthdate").dt().year().alias("birth_year"),
(col("weight") / col("height").pow(2)).alias("bmi"),
])
.collect()?;
println!("Column selection:");
print!("{result}\n");
Ok(())
}Column selection: shape: (4, 3) โโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโฌโโโโโโโโโโโโ โ name โ birth_year โ bmi โ โ --- โ --- โ --- โ โ str โ i32 โ f64 โ โโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโชโโโโโโโโโโโโก โ Alice Archer โ 1997 โ 23.791913 โ โ Ben Brown โ 1985 โ 23.141498 โ โ Chloe Cooper โ 1997 โ 20.055096 โ โ Daniel Donovan โ 1997 โ 27.134694 โ โโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโโโโ
Adding columns
//! ```cargo
//! [dependencies]
//! chrono = "0.4.45"
//! polars = { version = "0.54.4", features = ["lazy", "temporal", "sql"] }
//! ```
use chrono::NaiveDate;
use polars::{
df,
error::PolarsError,
frame::{DataFrame},
prelude::{LazyFrame, IntoLazy, col},
};
fn main() -> Result<(), PolarsError> {
let mut 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(1997, 3, 22).unwrap(),
NaiveDate::from_ymd_opt(1997, 4, 30).unwrap(),
],
"weight" => [57.9, 72.5, 54.6, 83.1], // (kg)
"height" => [1.56, 1.77, 1.65, 1.75], // (m)
)
.unwrap();
let result = df
.clone()
.lazy()
.with_columns([
col("birthdate").dt().year().alias("birth_year"),
(col("weight") / col("height").pow(2)).alias("bmi"),
])
.collect()?;
println!("With added colums:");
print!("{result}\n");
Ok(())
}With added colums: shape: (4, 6) โโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโฌโโโโโโโโโฌโโโโโโโโโฌโโโโโโโโโโโโโฌโโโโโโโโโโโโ โ name โ birthdate โ weight โ height โ birth_year โ bmi โ โ --- โ --- โ --- โ --- โ --- โ --- โ โ str โ date โ f64 โ f64 โ i32 โ f64 โ โโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโชโโโโโโโโโชโโโโโโโโโชโโโโโโโโโโโโโชโโโโโโโโโโโโก โ Alice Archer โ 1997-01-10 โ 57.9 โ 1.56 โ 1997 โ 23.791913 โ โ Ben Brown โ 1985-02-15 โ 72.5 โ 1.77 โ 1985 โ 23.141498 โ โ Chloe Cooper โ 1997-03-22 โ 54.6 โ 1.65 โ 1997 โ 20.055096 โ โ Daniel Donovan โ 1997-04-30 โ 83.1 โ 1.75 โ 1997 โ 27.134694 โ โโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโดโโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโโโโ
Expression expansion
lit means literal and it is part of the lazy expression API of Polars3's lazy feature.
//! ```cargo
//! [dependencies]
//! chrono = "0.4.45"
//! polars = { version = "0.54.4", features = ["lazy", "temporal", "sql"] }
//! ```
use chrono::NaiveDate;
use polars::{
df,
error::PolarsError,
frame::DataFrame,
prelude::{IntoLazy, col, cols, lit, RoundMode},
};
fn main() -> Result<(), PolarsError> {
let mut 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(1997, 3, 22).unwrap(),
NaiveDate::from_ymd_opt(1997, 4, 30).unwrap(),
],
"weight" => [57.9, 72.5, 54.6, 83.1], // (kg)
"height" => [1.56, 1.77, 1.65, 1.75], // (m)
)
.unwrap();
let result = df
.clone()
.lazy()
.select([
col("name"),
(cols(["weight", "height"]).as_expr() * lit(0.95))
.round(2, RoundMode::default())
.name()
.suffix("-5%"),
])
.collect()?;
println!("Transform:");
print!("{result}\n");
Ok(())
}Transform: shape: (4, 3) โโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโฌโโโโโโโโโโโโ โ name โ weight-5% โ height-5% โ โ --- โ --- โ --- โ โ str โ f64 โ f64 โ โโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโชโโโโโโโโโโโโก โ Alice Archer โ 55.0 โ 1.48 โ โ Ben Brown โ 68.88 โ 1.68 โ โ Chloe Cooper โ 51.87 โ 1.57 โ โ Daniel Donovan โ 78.94 โ 1.66 โ โโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโดโโโโโโโโโโโโ
Filtering rows
//! ```cargo
//! [dependencies]
//! chrono = "0.4.45"
//! polars = { version = "0.54.4", features = ["lazy", "temporal", "is_between", "sql"] }
//! ```
use chrono::NaiveDate;
use polars::{
df,
error::PolarsError,
frame::{DataFrame},
prelude::{IntoLazy, col, lit, ClosedInterval},
};
fn main() -> Result<(), PolarsError> {
let mut 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(1997, 3, 22).unwrap(),
NaiveDate::from_ymd_opt(1997, 4, 30).unwrap(),
],
"weight" => [57.9, 72.5, 54.6, 83.1], // (kg)
"height" => [1.56, 1.77, 1.65, 1.75], // (m)
)
.unwrap();
let result = df
.clone()
.lazy()
.filter(col("birthdate").dt().year().lt(lit(1990)))
.collect()?;
println!("With row filtering:");
print!("{result}\n");
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!("With complex row filtering:");
print!("{result}\n");
Ok(())
}With row filtering: shape: (1, 4) โโโโโโโโโโโโโฌโโโโโโโโโโโโโฌโโโโโโโโโฌโโโโโโโโโ โ name โ birthdate โ weight โ height โ โ --- โ --- โ --- โ --- โ โ str โ date โ f64 โ f64 โ โโโโโโโโโโโโโชโโโโโโโโโโโโโชโโโโโโโโโชโโโโโโโโโก โ Ben Brown โ 1985-02-15 โ 72.5 โ 1.77 โ โโโโโโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโดโโโโโโโโโ With complex row filtering: shape: (1, 4) โโโโโโโโโโโโโฌโโโโโโโโโโโโโฌโโโโโโโโโฌโโโโโโโโโ โ name โ birthdate โ weight โ height โ โ --- โ --- โ --- โ --- โ โ str โ date โ f64 โ f64 โ โโโโโโโโโโโโโชโโโโโโโโโโโโโชโโโโโโโโโชโโโโโโโโโก โ Ben Brown โ 1985-02-15 โ 72.5 โ 1.77 โ โโโโโโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโดโโโโโโโโโ
Grouping by
//! ```cargo
//! [dependencies]
//! chrono = "0.4.45"
//! polars = { version = "0.54.4", features = ["lazy", "temporal", "sql"] }
//! ```
use chrono::NaiveDate;
use polars::{
df,
error::PolarsError,
frame::DataFrame,
prelude::{IntoLazy, col, lit, len, RoundMode},
};
fn main() -> Result<(), PolarsError> {
let mut 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(1997, 3, 22).unwrap(),
NaiveDate::from_ymd_opt(1997, 4, 30).unwrap(),
],
"weight" => [57.9, 72.5, 54.6, 83.1], // (kg)
"height" => [1.56, 1.77, 1.65, 1.75], // (m)
)
.unwrap();
let result = df
.clone()
.lazy()
.group_by([(col("birthdate").dt().year() / lit(10) * lit(10)).alias("decade")])
.agg([len()])
.collect()?;
println!("Grouping by birth decade:");
print!("{result}\n");
let result = df
.clone()
.lazy()
.group_by([(col("birthdate").dt().year() / lit(10) * lit(10)).alias("decade")])
.agg([
len().alias("sample_size"),
col("weight")
.mean()
.round(2, RoundMode::default())
.alias("avg_weight"),
col("height").max().alias("tallest"),
])
.collect()?;
println!("Grouping by derived features:");
println!("{result}");
Ok(())
}Grouping by birth decade: shape: (2, 2) โโโโโโโโโโฌโโโโโโ โ decade โ len โ โ --- โ --- โ โ i32 โ u32 โ โโโโโโโโโโชโโโโโโก โ 1990 โ 3 โ โ 1980 โ 1 โ โโโโโโโโโโดโโโโโโ Grouping by derived features: shape: (2, 4) โโโโโโโโโโฌโโโโโโโโโโโโโโฌโโโโโโโโโโโโโฌโโโโโโโโโโ โ decade โ sample_size โ avg_weight โ tallest โ โ --- โ --- โ --- โ --- โ โ i32 โ u32 โ f64 โ f64 โ โโโโโโโโโโชโโโโโโโโโโโโโโชโโโโโโโโโโโโโชโโโโโโโโโโก โ 1990 โ 3 โ 65.2 โ 1.75 โ โ 1980 โ 1 โ 72.5 โ 1.77 โ โโโโโโโโโโดโโโโโโโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโโ
Data Analysis
When we receive a new dataset, the goal is not to immediately build charts or run models. The first goal is to understand whether the data can be trusted. The complete analysis is on github.
1. Inspect the raw data:
Download the data, load it with Polars3 and then print the head
//! ```cargo
//! [dependencies]
//! chrono = "0.4.45"
//! polars = { version = "0.54.4", features = ["lazy", "temporal", "sql", "csv"] }
//! ```
use polars::{
error::PolarsError,
prelude::{CsvParseOptions, CsvReadOptions, SerReader},
};
fn main() -> Result<(), PolarsError> {
let df_csv = CsvReadOptions::default()
.with_has_header(true)
.with_parse_options(CsvParseOptions::default().with_try_parse_dates(true))
.try_into_reader_with_file_path(Some(
"data/Water Quality Monitoring Dataset_ Ireland.csv".into(),
))?
.finish()?;
println!("{df_csv}");
Ok(())
}
rust-script failed with exit code 1
[stderr]
Error: ComputeError(ErrString("could not parse `50.5` as dtype `i64` at column 'Alkalinity-total (as CaCO3)' (column number 4)\n\nThe current offset in the file is 7606 bytes.\n\nYou might want to try:\n- increasing `infer_schema_length` (e.g. `infer_schema_length=10000`),\n- specifying correct dtype with the `schema_overrides` argument\n- setting `ignore_errors` to `True`,\n- adding `50.5` to the `null_values` list.\n\nOriginal error: ```invalid primitive value found during CSV parsing```"))
Polars3 is not guessing the type of some of the columns correctly. Let's allow it to guess from 100 rows by default.
//! ```cargo
//! [dependencies]
//! chrono = "0.4.45"
//! polars = { version = "0.54.4", features = ["lazy", "temporal", "sql", "csv"] }
//! ```
use polars::{
error::PolarsError,
prelude::{CsvParseOptions, CsvReadOptions, SerReader},
};
fn main() -> Result<(), PolarsError> {
let df_csv = CsvReadOptions::default()
.with_has_header(true)
.with_infer_schema_length(None)
.with_parse_options(CsvParseOptions::default().with_try_parse_dates(true))
.try_into_reader_with_file_path(Some(
"data/Water Quality Monitoring Dataset_ Ireland.csv".into(),
))?
.finish()?;
println!("{df_csv}");
Ok(())
}shape: (29_159, 14) โโโโโโโโโโโโโโโโฌโโโโโโโโฌโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโฌโโโโฌโโโโโโโฌโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโฌโโโโโโโโโ โ WaterbodyNam โ Years โ SampleDate โ Alkalinity-t โ โฆ โ pH โ Temperature โ Total โ True โ โ e โ --- โ --- โ otal (as โ โ --- โ --- โ Hardness โ Colour โ โ --- โ i64 โ str โ CaCO3) โ โ f64 โ f64 โ (as CaCO3) โ --- โ โ str โ โ โ --- โ โ โ โ --- โ f64 โ โ โ โ โ f64 โ โ โ โ f64 โ โ โโโโโโโโโโโโโโโโชโโโโโโโโชโโโโโโโโโโโโโชโโโโโโโโโโโโโโโชโโโโชโโโโโโโชโโโโโโโโโโโโโโชโโโโโโโโโโโโโโชโโโโโโโโโก โ ABBEYTOWN_01 โ 2023 โ Feb โ 314.0 โ โฆ โ 7.8 โ 10.4 โ 370.0 โ 24.0 โ โ 0 โ โ โ โ โ โ โ โ โ โ Allua โ 2007 โ Aug โ 14.0 โ โฆ โ 7.42 โ 17.8 โ 13.4 โ 35.0 โ โ Allua โ 2007 โ Aug โ 17.0 โ โฆ โ 7.67 โ 18.1 โ 15.8 โ 29.0 โ โ Allua โ 2007 โ Aug โ 18.0 โ โฆ โ 7.63 โ 17.8 โ 15.9 โ 31.0 โ โ Allua โ 2007 โ Sep โ 19.0 โ โฆ โ 7.33 โ 20.1 โ 15.4 โ 23.0 โ โ โฆ โ โฆ โ โฆ โ โฆ โ โฆ โ โฆ โ โฆ โ โฆ โ โฆ โ โ SULLANE_060 โ 2022 โ Sep โ 31.0 โ โฆ โ 7.1 โ 14.9 โ 45.0 โ 27.0 โ โ SULLANE_060 โ 2022 โ Nov โ 22.0 โ โฆ โ 6.9 โ 12.3 โ 34.0 โ 58.0 โ โ SULLANE_060 โ 2023 โ Mar โ 36.0 โ โฆ โ 7.2 โ 7.1 โ 44.0 โ 20.0 โ โ TWO POT โ 2023 โ Feb โ 81.0 โ โฆ โ 7.4 โ 8.6 โ 120.0 โ 9.0 โ โ (Cork โ โ โ โ โ โ โ โ โ โ City)_010 โ โ โ โ โ โ โ โ โ โ TWO POT โ 2023 โ Feb โ 82.0 โ โฆ โ 7.8 โ 8.1 โ 121.0 โ 5.0 โ โ (Cork โ โ โ โ โ โ โ โ โ โ City)_010 โ โ โ โ โ โ โ โ โ โโโโโโโโโโโโโโโโดโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโโโโโโโดโโโโดโโโโโโโดโโโโโโโโโโโโโโดโโโโโโโโโโโโโโดโโโโโโโโโ
Let us have Polars4 infer the proper types of the columns now from 10000 rows
//! ```cargo
//! [dependencies]
//! chrono = "0.4.45"
//! polars = { version = "0.54.4", features = ["lazy", "temporal", "sql", "csv"] }
//! ```
use polars::{
error::PolarsError,
prelude::{CsvParseOptions, CsvReadOptions, SerReader},
};
fn main() -> Result<(), PolarsError> {
let df_csv = CsvReadOptions::default()
.with_has_header(true)
.with_infer_schema_length(Some(10_000))
.with_parse_options(CsvParseOptions::default().with_try_parse_dates(true))
.try_into_reader_with_file_path(Some(
"data/Water Quality Monitoring Dataset_ Ireland.csv".into(),
))?
.finish()?;
println!("{df_csv}");
Ok(())
}shape: (29_159, 14) โโโโโโโโโโโโโโโโฌโโโโโโโโฌโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโฌโโโโฌโโโโโโโฌโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโฌโโโโโโโโโ โ WaterbodyNam โ Years โ SampleDate โ Alkalinity-t โ โฆ โ pH โ Temperature โ Total โ True โ โ e โ --- โ --- โ otal (as โ โ --- โ --- โ Hardness โ Colour โ โ --- โ i64 โ str โ CaCO3) โ โ f64 โ f64 โ (as CaCO3) โ --- โ โ str โ โ โ --- โ โ โ โ --- โ f64 โ โ โ โ โ f64 โ โ โ โ f64 โ โ โโโโโโโโโโโโโโโโชโโโโโโโโชโโโโโโโโโโโโโชโโโโโโโโโโโโโโโชโโโโชโโโโโโโชโโโโโโโโโโโโโโชโโโโโโโโโโโโโโชโโโโโโโโโก โ ABBEYTOWN_01 โ 2023 โ Feb โ 314.0 โ โฆ โ 7.8 โ 10.4 โ 370.0 โ 24.0 โ โ 0 โ โ โ โ โ โ โ โ โ โ Allua โ 2007 โ Aug โ 14.0 โ โฆ โ 7.42 โ 17.8 โ 13.4 โ 35.0 โ โ Allua โ 2007 โ Aug โ 17.0 โ โฆ โ 7.67 โ 18.1 โ 15.8 โ 29.0 โ โ Allua โ 2007 โ Aug โ 18.0 โ โฆ โ 7.63 โ 17.8 โ 15.9 โ 31.0 โ โ Allua โ 2007 โ Sep โ 19.0 โ โฆ โ 7.33 โ 20.1 โ 15.4 โ 23.0 โ โ โฆ โ โฆ โ โฆ โ โฆ โ โฆ โ โฆ โ โฆ โ โฆ โ โฆ โ โ SULLANE_060 โ 2022 โ Sep โ 31.0 โ โฆ โ 7.1 โ 14.9 โ 45.0 โ 27.0 โ โ SULLANE_060 โ 2022 โ Nov โ 22.0 โ โฆ โ 6.9 โ 12.3 โ 34.0 โ 58.0 โ โ SULLANE_060 โ 2023 โ Mar โ 36.0 โ โฆ โ 7.2 โ 7.1 โ 44.0 โ 20.0 โ โ TWO POT โ 2023 โ Feb โ 81.0 โ โฆ โ 7.4 โ 8.6 โ 120.0 โ 9.0 โ โ (Cork โ โ โ โ โ โ โ โ โ โ City)_010 โ โ โ โ โ โ โ โ โ โ TWO POT โ 2023 โ Feb โ 82.0 โ โฆ โ 7.8 โ 8.1 โ 121.0 โ 5.0 โ โ (Cork โ โ โ โ โ โ โ โ โ โ City)_010 โ โ โ โ โ โ โ โ โ โโโโโโโโโโโโโโโโดโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโโโโโโโดโโโโดโโโโโโโดโโโโโโโโโโโโโโดโโโโโโโโโโโโโโดโโโโโโโโโ
use polars::{
error::PolarsResult,
io::{
SerReader,
csv::read::{CsvParseOptions, CsvReadOptions},
},
};
use data_pipeline::quality_flow::inspect_raw_data;
fn main() -> PolarsResult<()> {
let df = CsvReadOptions::default()
.with_has_header(true)
// Discovery step: scan the file because we do not know columns yet.
.with_infer_schema_length(Some(10_000))
.with_parse_options(CsvParseOptions::default().with_try_parse_dates(true))
.try_into_reader_with_file_path(Some(
"data/Water Quality Monitoring Dataset_ Ireland.csv".into(),
))?
.finish()?;
// 1. Inspect the raw data
inspect_raw_data(df)?;
Ok(())
} cargo run --bin inspect_raw_data============================================================ 1. Inspect the raw data ============================================================ raw dataset size: This confirms how many rows and columns were loaded from the CSV. shape: (2, 2) โโโโโโโโโโโฌโโโโโโโโ โ metric โ value โ โ --- โ --- โ โ str โ i64 โ โโโโโโโโโโโชโโโโโโโโก โ rows โ 29159 โ โ columns โ 14 โ โโโโโโโโโโโดโโโโโโโโ inferred schema: This shows each column name and the type Polars inferred from the file. shape: (14, 3) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโ โ column โ inferred_type โ storage_kind โ โ --- โ --- โ --- โ โ str โ str โ str โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโก โ WaterbodyName โ String โ text or mixed โ โ Years โ Int64 โ number โ โ SampleDate โ String โ text or mixed โ โ Alkalinity-total (as CaCO3) โ Float64 โ number โ โ Ammonia-Total (as N) โ Float64 โ number โ โ โฆ โ โฆ โ โฆ โ โ ortho-Phosphate (as P) - unspeโฆ โ Float64 โ number โ โ pH โ Float64 โ number โ โ Temperature โ Float64 โ number โ โ Total Hardness (as CaCO3) โ Float64 โ number โ โ True Colour โ Float64 โ number โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโ raw row sample: This shows one original wide record before any reshaping. shape: (1, 14) โโโโโโโโโโโโโโโโฌโโโโโโโโฌโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโฌโโโโฌโโโโโโฌโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโฌโโโโโโโโโ โ WaterbodyNam โ Years โ SampleDate โ Alkalinity-t โ โฆ โ pH โ Temperature โ Total โ True โ โ e โ --- โ --- โ otal (as โ โ --- โ --- โ Hardness โ Colour โ โ --- โ i64 โ str โ CaCO3) โ โ f64 โ f64 โ (as CaCO3) โ --- โ โ str โ โ โ --- โ โ โ โ --- โ f64 โ โ โ โ โ f64 โ โ โ โ f64 โ โ โโโโโโโโโโโโโโโโชโโโโโโโโชโโโโโโโโโโโโโชโโโโโโโโโโโโโโโชโโโโชโโโโโโชโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโชโโโโโโโโโก โ ABBEYTOWN_01 โ 2023 โ Feb โ 314.0 โ โฆ โ 7.8 โ 10.4 โ 370.0 โ 24.0 โ โ 0 โ โ โ โ โ โ โ โ โ โโโโโโโโโโโโโโโโดโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโโโโโโโดโโโโดโโโโโโดโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโดโโโโโโโโโ first-pass column roles: This separates location/date columns from measurement columns. shape: (14, 2) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโ โ column โ role โ โ --- โ --- โ โ str โ str โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโก โ WaterbodyName โ location โ โ Years โ date โ โ SampleDate โ date โ โ Alkalinity-total (as CaCO3) โ measurement โ โ Ammonia-Total (as N) โ measurement โ โ โฆ โ โฆ โ โ ortho-Phosphate (as P) - unspeโฆ โ measurement โ โ pH โ measurement โ โ Temperature โ measurement โ โ Total Hardness (as CaCO3) โ measurement โ โ True Colour โ measurement โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโ long-form sample: This previews the wide measurements as parameter/value rows. shape: (5, 7) โโโโโโโโโโโโโโโโโฌโโโโโโโโฌโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโ โ WaterbodyName โ Years โ SampleDate โ source_column โ measurement_va โ parameter โ unit โ โ --- โ --- โ --- โ --- โ lue โ --- โ --- โ โ str โ i64 โ str โ str โ --- โ str โ str โ โ โ โ โ โ f64 โ โ โ โโโโโโโโโโโโโโโโโชโโโโโโโโชโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโชโโโโโโโโโโโก โ ABBEYTOWN_010 โ 2023 โ Feb โ Alkalinity-tot โ 314.0 โ Alkalinity-tot โ as CaCO3 โ โ โ โ โ al (as CaCO3) โ โ al โ โ โ Allua โ 2007 โ Aug โ Alkalinity-tot โ 14.0 โ Alkalinity-tot โ as CaCO3 โ โ โ โ โ al (as CaCO3) โ โ al โ โ โ Allua โ 2007 โ Aug โ Alkalinity-tot โ 17.0 โ Alkalinity-tot โ as CaCO3 โ โ โ โ โ al (as CaCO3) โ โ al โ โ โ Allua โ 2007 โ Aug โ Alkalinity-tot โ 18.0 โ Alkalinity-tot โ as CaCO3 โ โ โ โ โ al (as CaCO3) โ โ al โ โ โ Allua โ 2007 โ Sep โ Alkalinity-tot โ 19.0 โ Alkalinity-tot โ as CaCO3 โ โ โ โ โ al (as CaCO3) โ โ al โ โ โโโโโโโโโโโโโโโโโดโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโดโโโโโโโโโโโ
2. Profile the data
This gives us a first picture of the dataset before making decisions.
use polars::{
error::PolarsResult,
io::{
SerReader,
csv::read::{CsvParseOptions, CsvReadOptions},
},
};
use data_pipeline::quality_flow::profile_the_data;
fn main() -> PolarsResult<()> {
let df = CsvReadOptions::default()
.with_has_header(true)
// Discovery step: scan the file because we do not know columns yet.
.with_infer_schema_length(Some(10_000))
.with_parse_options(CsvParseOptions::default().with_try_parse_dates(true))
.try_into_reader_with_file_path(Some(
"data/Water Quality Monitoring Dataset_ Ireland.csv".into(),
))?
.finish()?;
// 2. Profile the data
profile_the_data(df)?;
Ok(())
} cargo run --bin profile_the_data============================================================ 2. Profile the data ============================================================ profile scope: This repeats the dataset size before summarizing each important column. shape: (2, 2) โโโโโโโโโโโฌโโโโโโโโ โ metric โ value โ โ --- โ --- โ โ str โ i64 โ โโโโโโโโโโโชโโโโโโโโก โ rows โ 29159 โ โ columns โ 14 โ โโโโโโโโโโโดโโโโโโโโ date coverage: This combines Years and SampleDate into a usable month-level date range. shape: (5, 2) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโ โ metric โ value โ โ --- โ --- โ โ str โ str โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโก โ earliest_date โ 2007-01-01 โ โ latest_date โ 2023-04-01 โ โ invalid_dates โ 0 โ โ missing_dates โ 0 โ โ gaps_over_time_gt_31_days โ 0 โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโ column profile: This gives missing counts, distinct counts, numeric ranges, averages, and notes. shape: (14, 9) โโโโโโโโโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโโฌโโโโโโโโโโฌโโโโฌโโโโโโโโโโฌโโโโโโโโโโฌโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโ โ column โ role โ type โ missing โ โฆ โ minimum โ maximum โ average โ notes โ โ --- โ --- โ --- โ --- โ โ --- โ --- โ --- โ --- โ โ str โ str โ str โ i64 โ โ str โ str โ str โ str โ โโโโโโโโโโโโโโโโชโโโโโโโโโโโชโโโโโโโโโโชโโโโโโโโโโชโโโโชโโโโโโโโโโชโโโโโโโโโโชโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโก โ WaterbodyNam โ location โ String โ 0 โ โฆ โ โ โ โ unique โ โ e โ โ โ โ โ โ โ โ locations: โ โ โ โ โ โ โ โ โ โ 160 โ โ Years โ date โ Int64 โ 0 โ โฆ โ 2007 โ 2023 โ Float64(2014 โ included in โ โ โ โ โ โ โ โ โ .78253712404 โ combined โ โ โ โ โ โ โ โ โ 4) โ date coveโฆ โ โ SampleDate โ date โ String โ 0 โ โฆ โ โ โ โ review full โ โ โ โ โ โ โ โ โ โ category โ โ โ โ โ โ โ โ โ โ list; incโฆ โ โ Alkalinity-t โ numeric โ Float64 โ 0 โ โฆ โ 0 โ 442 โ Float64(139. โ โ โ otal (as โ โ โ โ โ โ โ 858347851435 โ โ โ CaCO3) โ โ โ โ โ โ โ 2) โ โ โ Ammonia-Tota โ numeric โ Float64 โ 0 โ โฆ โ 0 โ 40 โ Float64(0.06 โ โ โ l (as N) โ โ โ โ โ โ โ 357266127096 โ โ โ โ โ โ โ โ โ โ 262) โ โ โ โฆ โ โฆ โ โฆ โ โฆ โ โฆ โ โฆ โ โฆ โ โฆ โ โฆ โ โ ortho-Phosph โ numeric โ Float64 โ 0 โ โฆ โ -0.004 โ 70 โ Float64(0.06 โ negative โ โ ate (as P) - โ โ โ โ โ โ โ 878934462773 โ value found โ โ unspeโฆ โ โ โ โ โ โ โ 074) โ (-0.004) โ โ pH โ numeric โ Float64 โ 0 โ โฆ โ 4.7 โ 9.8 โ Float64(7.55 โ โ โ โ โ โ โ โ โ โ 205686066051 โ โ โ โ โ โ โ โ โ โ 8) โ โ โ Temperature โ numeric โ Float64 โ 0 โ โฆ โ 0.6 โ 637 โ Float64(10.8 โ โ โ โ โ โ โ โ โ โ 505031036729 โ โ โ โ โ โ โ โ โ โ 74) โ โ โ Total โ numeric โ Float64 โ 0 โ โฆ โ 0 โ 642 โ Float64(159. โ โ โ Hardness (as โ โ โ โ โ โ โ 092110326142 โ โ โ CaCO3) โ โ โ โ โ โ โ 9) โ โ โ True Colour โ numeric โ Float64 โ 0 โ โฆ โ 0 โ 953 โ Float64(58.1 โ โ โ โ โ โ โ โ โ โ 374635618505 โ โ โ โ โ โ โ โ โ โ 45) โ โ โโโโโโโโโโโโโโโโดโโโโโโโโโโโดโโโโโโโโโโดโโโโโโโโโโดโโโโดโโโโโโโโโโดโโโโโโโโโโดโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโ text/category profile: This summarizes unique text values and possible spelling variations. shape: (2, 5) โโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ column โ empty_values โ unique_values โ sample_unique_values โ possible_spelling_variatio โ โ --- โ --- โ --- โ --- โ ns โ โ str โ i64 โ i64 โ str โ --- โ โ โ โ โ โ str โ โโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโโโโโโโโโโโโก โ WaterbodyName โ 0 โ 160 โ ABBEYTOWN_010, โ โ โ โ โ โ ASKANAGAP STREAโฆ โ โ โ SampleDate โ 0 โ 12 โ Apr, Aug, Dec, Feb, โ โ โ โ โ โ Jan, Jul, โฆ โ โ โโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
3. Identify data quality problems
use polars::{
error::PolarsResult,
io::{
SerReader,
csv::read::{CsvParseOptions, CsvReadOptions},
},
};
use data_pipeline::quality_flow::identify_data_quality_problems;
fn main() -> PolarsResult<()> {
let df = CsvReadOptions::default()
.with_has_header(true)
// Discovery step: scan the file because we do not know columns yet.
.with_infer_schema_length(Some(10_000))
.with_parse_options(CsvParseOptions::default().with_try_parse_dates(true))
.try_into_reader_with_file_path(Some(
"data/Water Quality Monitoring Dataset_ Ireland.csv".into(),
))?
.finish()?;
// 3. Identify data quality problems
identify_data_quality_problems(df)?;
Ok(())
} cargo run --bin identify_data_quality_problems============================================================ 3. Identify data quality problems ============================================================ data quality summary: This is the high-level checklist of problems that could make analysis unreliable. shape: (13, 4) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโฌโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ check โ count โ status โ note โ โ --- โ --- โ --- โ --- โ โ str โ i64 โ str โ str โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโชโโโโโโโโโชโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโก โ measurement columns checked โ 11 โ info โ wide measurement columns becomโฆ โ โ missing values โ 0 โ ok โ null values across raw columns โ โ duplicate rows โ 14478 โ review โ exact raw-row duplicates โ โ numeric values stored as text โ 0 โ ok โ string columns whose values arโฆ โ โ invalid date values โ 0 โ ok โ date-like values that failed pโฆ โ โ โฆ โ โฆ โ โฆ โ โฆ โ โ pH outside 0-14 โ 0 โ ok โ domain rule for pH โ โ negative concentration-like meโฆ โ 2 โ review โ negative values outside pH andโฆ โ โ outlier values โ 12652 โ review โ IQR outliers across 8 columns โ โ duplicate location/date/parameโฆ โ 204237 โ review โ same location, date, and paramโฆ โ โ large gaps in time series โ 5395 โ review โ location time periods with misโฆ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโดโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ data quality details: This gives the columns and counts behind the summary checks. shape: (9, 4) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ problem โ column โ count โ note โ โ --- โ --- โ --- โ --- โ โ str โ str โ i64 โ str โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโชโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโก โ outliers โ Chloride โ 1861 โ outside [5.999999999999998, โ โ โ โ โ 31โฆ โ โ outliers โ Conductivity @25ยฐC โ 20 โ outside [-179, 909] by IQR โ โ โ โ โ rulโฆ โ โ outliers โ Dissolved Oxygen โ 2314 โ outside โ โ โ โ โ [10.999999999999993, 1โฆ โ โ outliers โ ortho-Phosphate (as P) - โ 6229 โ outside โ โ โ unspeโฆ โ โ [0.009500000000000005,โฆ โ โ negative concentration-like โ ortho-Phosphate (as P) - โ 2 โ negative value outside pH โ โ meโฆ โ unspeโฆ โ โ and โฆ โ โ outliers โ pH โ 406 โ outside [6, 9.2] by IQR โ โ โ โ โ rule โ โ outliers โ Temperature โ 134 โ outside โ โ โ โ โ [0.8499999999999988, 2โฆ โ โ outliers โ Total Hardness (as CaCO3) โ 3 โ outside [-178, 486] by IQR โ โ โ โ โ rulโฆ โ โ outliers โ True Colour โ 1685 โ outside [-48.5, 147.5] by โ โ โ โ โ IQR โฆ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ principle: This is the rule that guides the cleaning decision in the next step. shape: (1, 1) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ principle โ โ --- โ โ str โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโก โ Bad input should not quietly bโฆ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
4. Clean and normalize the data
use data_pipeline::quality_flow::clean_and_normalize_the_data;
use polars::{
error::PolarsResult,
io::{
SerReader,
csv::read::{CsvParseOptions, CsvReadOptions},
},
};
fn main() -> PolarsResult<()> {
let df = CsvReadOptions::default()
.with_has_header(true)
// Discovery step: scan the file because we do not know columns yet.
.with_infer_schema_length(Some(10_000))
.with_parse_options(CsvParseOptions::default().with_try_parse_dates(true))
.try_into_reader_with_file_path(Some(
"data/Water Quality Monitoring Dataset_ Ireland.csv".into(),
))?
.finish()?;
// 4. Clean and normalize the data
clean_and_normalize_the_data(df)?;
Ok(())
} cargo run --bin clean_and_normalize_the_data============================================================ 4. Clean and normalize the data ============================================================ cleaning summary: This shows how many normalized rows were kept, rejected, or deduplicated. shape: (3, 2) โโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโ โ metric โ value โ โ --- โ --- โ โ str โ i64 โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโก โ cleaned_rows โ 148372 โ โ invalid_rows โ 2 โ โ exact_duplicates_removed โ 172375 โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโ cleaned sample: This is the normalized long-form data that is easier to query and visualize. shape: (5, 10) โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโฌโโโโโโโฌโโโโฌโโโโโโโโโโโโโฌโโโโโโโโโโโโโฌโโโโโโโโฌโโโโโโโโโโโโโ โ source_row โ location โ sample_date โ year โ โฆ โ parameter_ โ unit โ value โ source_col โ โ --- โ --- โ --- โ --- โ โ code โ --- โ --- โ umn โ โ i64 โ str โ str โ i32 โ โ --- โ str โ f64 โ --- โ โ โ โ โ โ โ str โ โ โ str โ โโโโโโโโโโโโโโชโโโโโโโโโโโโโโชโโโโโโโโโโโโโโชโโโโโโโชโโโโชโโโโโโโโโโโโโชโโโโโโโโโโโโโชโโโโโโโโชโโโโโโโโโโโโโก โ 1 โ ABBEYTOWN_0 โ 2023-02-01 โ 2023 โ โฆ โ ALKALINITY โ as CaCO3 โ 314.0 โ Alkalinity โ โ โ 10 โ โ โ โ -TOTAL โ โ โ -total (as โ โ โ โ โ โ โ โ โ โ CaCO3) โ โ 1 โ ABBEYTOWN_0 โ 2023-02-01 โ 2023 โ โฆ โ AMMONIA-TO โ as N โ 0.033 โ Ammonia-To โ โ โ 10 โ โ โ โ TAL โ โ โ tal (as N) โ โ 1 โ ABBEYTOWN_0 โ 2023-02-01 โ 2023 โ โฆ โ BOD_-_5_DA โ Total โ 1.2 โ BOD - 5 โ โ โ 10 โ โ โ โ YS โ โ โ days โ โ โ โ โ โ โ โ โ โ (Total) โ โ 1 โ ABBEYTOWN_0 โ 2023-02-01 โ 2023 โ โฆ โ CHLORIDE โ not_encode โ 27.3 โ Chloride โ โ โ 10 โ โ โ โ โ d โ โ โ โ 1 โ ABBEYTOWN_0 โ 2023-02-01 โ 2023 โ โฆ โ CONDUCTIVI โ @25ยฐC โ 711.0 โ Conductivi โ โ โ 10 โ โ โ โ TY โ โ โ ty @25ยฐC โ โโโโโโโโโโโโโโดโโโโโโโโโโโโโโดโโโโโโโโโโโโโโดโโโโโโโดโโโโดโโโโโโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโดโโโโโโโโโโโโโ invalid rows sample: These rows were separated so bad input does not become trusted data. shape: (2, 6) โโโโโโโโโโโโโโฌโโโโโโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโ โ source_row โ location โ raw_date โ source_column โ raw_value โ invalid_reason โ โ --- โ --- โ --- โ --- โ --- โ --- โ โ i64 โ str โ str โ str โ str โ str โ โโโโโโโโโโโโโโชโโโโโโโโโโโโโชโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโโโโโโโโก โ 111 โ ASKANAGAP โ Jan โ ortho-Phosphate (as P) โ -0.004 โ negative โ โ โ STREAM_010 โ โ - unspeโฆ โ โ concentration-like meโฆ โ โ 15723 โ ASKANAGAP โ Jan โ ortho-Phosphate (as P) โ -0.004 โ negative โ โ โ STREAM_010 โ โ - unspeโฆ โ โ concentration-like meโฆ โ โโโโโโโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโ
5. Handle missing values carefully
use data_pipeline::quality_flow::handle_missing_values_carefully;
use polars::{
error::PolarsResult,
io::{
SerReader,
csv::read::{CsvParseOptions, CsvReadOptions},
},
};
fn main() -> PolarsResult<()> {
let df = CsvReadOptions::default()
.with_has_header(true)
// Discovery step: scan the file because we do not know columns yet.
.with_infer_schema_length(Some(10_000))
.with_parse_options(CsvParseOptions::default().with_try_parse_dates(true))
.try_into_reader_with_file_path(Some(
"data/Water Quality Monitoring Dataset_ Ireland.csv".into(),
))?
.finish()?;
// 5. Handle missing values carefully
handle_missing_values_carefully(df)?;
Ok(())
} cargo run --bin handle_missing_values_carefully============================================================ 5. Handle missing values carefully ============================================================ missing-value decision summary: This separates invalid data, missing data, gap candidates, and flagged observed values. shape: (7, 3) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ case โ count โ decision โ โ --- โ --- โ --- โ โ str โ i64 โ str โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโชโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโก โ invalid or impossible rows โ 2 โ quarantine โ โ missing critical fields โ 0 โ reject row โ โ missing measurement values โ 0 โ keep NULL unless safe to estimโฆ โ โ small time-series gaps โ 45165 โ candidate for interpolation afโฆ โ โ large time-series gaps โ 14179 โ keep missing โ โ suspicious but possible values โ 10677 โ keep observed value with qualiโฆ โ โ values filled automatically โ 0 โ none; filling is not automatic โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ quarantined row sample: These rows are not filled because a critical field or measurement value is missing or invalid. shape: (2, 6) โโโโโโโโโโโโโโฌโโโโโโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโ โ source_row โ location โ raw_date โ source_column โ raw_value โ decision โ โ --- โ --- โ --- โ --- โ --- โ --- โ โ i64 โ str โ str โ str โ str โ str โ โโโโโโโโโโโโโโชโโโโโโโโโโโโโชโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโโโโโโโโก โ 111 โ ASKANAGAP โ Jan โ ortho-Phosphate (as P) โ -0.004 โ negative โ โ โ STREAM_010 โ โ - unspeโฆ โ โ concentration-like meโฆ โ โ 15723 โ ASKANAGAP โ Jan โ ortho-Phosphate (as P) โ -0.004 โ negative โ โ โ STREAM_010 โ โ - unspeโฆ โ โ concentration-like meโฆ โ โโโโโโโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโ time-series gap examples: These are observed gaps; small gaps may be interpolated only after review. shape: (20, 6) โโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโฌโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโ โ location โ parameter_code โ from_date โ to_date โ missing_months โ decision โ โ --- โ --- โ --- โ --- โ --- โ --- โ โ str โ str โ str โ str โ i64 โ str โ โโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโชโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโโโโโโก โ ALLUA โ ALKALINITY-TOTAL โ 2007-09-01 โ 2008-01-01 โ 3 โ keep missing; gap is โ โ โ โ โ โ โ too large โ โ ALLUA โ ALKALINITY-TOTAL โ 2008-12-01 โ 2009-04-01 โ 3 โ keep missing; gap is โ โ โ โ โ โ โ too large โ โ ALLUA โ ALKALINITY-TOTAL โ 2009-04-01 โ 2009-06-01 โ 1 โ candidate for โ โ โ โ โ โ โ interpolation afโฆ โ โ ALLUA โ ALKALINITY-TOTAL โ 2009-06-01 โ 2009-08-01 โ 1 โ candidate for โ โ โ โ โ โ โ interpolation afโฆ โ โ ALLUA โ ALKALINITY-TOTAL โ 2009-08-01 โ 2009-10-01 โ 1 โ candidate for โ โ โ โ โ โ โ interpolation afโฆ โ โ โฆ โ โฆ โ โฆ โ โฆ โ โฆ โ โฆ โ โ ALLUA โ AMMONIA-TOTAL โ 2009-06-01 โ 2009-08-01 โ 1 โ candidate for โ โ โ โ โ โ โ interpolation afโฆ โ โ ALLUA โ AMMONIA-TOTAL โ 2009-08-01 โ 2009-10-01 โ 1 โ candidate for โ โ โ โ โ โ โ interpolation afโฆ โ โ ALLUA โ AMMONIA-TOTAL โ 2009-10-01 โ 2010-03-01 โ 4 โ keep missing; gap is โ โ โ โ โ โ โ too large โ โ ALLUA โ AMMONIA-TOTAL โ 2010-03-01 โ 2010-07-01 โ 3 โ keep missing; gap is โ โ โ โ โ โ โ too large โ โ ALLUA โ AMMONIA-TOTAL โ 2010-08-01 โ 2010-10-01 โ 1 โ candidate for โ โ โ โ โ โ โ interpolation afโฆ โ โโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโ quality-flagged sample: These observed values are kept, but marked because they need caution. shape: (10, 6) โโโโโโโโโโโโฌโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโ โ location โ sample_date โ parameter_code โ value โ quality_flag โ missing_decision โ โ --- โ --- โ --- โ --- โ --- โ --- โ โ str โ str โ str โ f64 โ str โ str โ โโโโโโโโโโโโชโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโชโโโโโโโโชโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโโโโโโโก โ ALLUA โ 2007-09-01 โ AMMONIA-TOTAL โ 0.066 โ suspicious_possible_o โ keep observed value โ โ โ โ โ โ utlier โ with qualiโฆ โ โ ALLUA โ 2008-01-01 โ AMMONIA-TOTAL โ 0.069 โ suspicious_possible_o โ keep observed value โ โ โ โ โ โ utlier โ with qualiโฆ โ โ ALLUA โ 2008-01-01 โ ORTHO-PHOSPHATE โ 0.005 โ suspicious_possible_o โ keep observed value โ โ โ โ โ โ utlier โ with qualiโฆ โ โ ALLUA โ 2008-01-01 โ AMMONIA-TOTAL โ 0.068 โ suspicious_possible_o โ keep observed value โ โ โ โ โ โ utlier โ with qualiโฆ โ โ ALLUA โ 2008-01-01 โ AMMONIA-TOTAL โ 0.067 โ suspicious_possible_o โ keep observed value โ โ โ โ โ โ utlier โ with qualiโฆ โ โ ALLUA โ 2008-02-01 โ AMMONIA-TOTAL โ 0.133 โ suspicious_possible_o โ keep observed value โ โ โ โ โ โ utlier โ with qualiโฆ โ โ ALLUA โ 2008-02-01 โ AMMONIA-TOTAL โ 0.111 โ suspicious_possible_o โ keep observed value โ โ โ โ โ โ utlier โ with qualiโฆ โ โ ALLUA โ 2008-02-01 โ AMMONIA-TOTAL โ 0.113 โ suspicious_possible_o โ keep observed value โ โ โ โ โ โ utlier โ with qualiโฆ โ โ ALLUA โ 2008-03-01 โ AMMONIA-TOTAL โ 0.04 โ suspicious_possible_o โ keep observed value โ โ โ โ โ โ utlier โ with qualiโฆ โ โ ALLUA โ 2008-03-01 โ ORTHO-PHOSPHATE โ 0.005 โ suspicious_possible_o โ keep observed value โ โ โ โ โ โ utlier โ with qualiโฆ โ โโโโโโโโโโโโดโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโ principle: This is the rule for deciding whether a missing value should be filled. shape: (1, 1) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ principle โ โ --- โ โ str โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโก โ Filling data is a decision, noโฆ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ handled data summary: This confirms the row counts after applying the missing-value decisions. shape: (3, 2) โโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโ โ metric โ value โ โ --- โ --- โ โ str โ i64 โ โโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโก โ handled_rows โ 148372 โ โ quarantined_rows โ 2 โ โ duplicates_removed โ 172375 โ โโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโ
6. Validate before storing
use data_pipeline::quality_flow::validate_before_storing;
use polars::{
error::PolarsResult,
io::{
SerReader,
csv::read::{CsvParseOptions, CsvReadOptions},
},
};
fn main() -> PolarsResult<()> {
let df = CsvReadOptions::default()
.with_has_header(true)
// Discovery step: scan the file because we do not know columns yet.
.with_infer_schema_length(Some(10_000))
.with_parse_options(CsvParseOptions::default().with_try_parse_dates(true))
.try_into_reader_with_file_path(Some(
"data/Water Quality Monitoring Dataset_ Ireland.csv".into(),
))?
.finish()?;
// 6. Validate before storing
validate_before_storing(df)?;
Ok(())
} cargo run --bin validate_before_storing============================================================ 6. Validate before storing ============================================================ validation rule summary: This shows the storage rules, how many records were checked, and what failed. shape: (7, 4) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโฌโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ rule โ checked โ failed โ action โ โ --- โ --- โ --- โ --- โ โ str โ i64 โ i64 โ str โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโชโโโโโโโโโชโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโก โ every measurement has a locatiโฆ โ 320749 โ 0 โ reject missing locations โ โ every measurement has a parameโฆ โ 320749 โ 0 โ reject missing parameters โ โ every measurement has a date โ 320749 โ 0 โ reject missing or invalid dateโฆ โ โ value is a valid number or expโฆ โ 320749 โ 0 โ store numeric values; store miโฆ โ โ known parameter respects range โ 320749 โ 20 โ reject impossible values for kโฆ โ โ exact duplicate records handleโฆ โ 320729 โ 172369 โ remove exact duplicates โ โ repeated measurements handled โฆ โ 148360 โ 31854 โ keep with source_row so repeatโฆ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโดโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ records rejected before storage: These rows failed validation and should not be inserted into trusted tables. shape: (10, 6) โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโ โ source_row โ location โ raw_date โ source_column โ raw_value โ rule_failed โ โ --- โ --- โ --- โ --- โ --- โ --- โ โ i64 โ str โ str โ str โ str โ str โ โโโโโโโโโโโโโโชโโโโโโโโโโโโโโโชโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโโโโโโโก โ 111 โ ASKANAGAP โ Jan โ ortho-Phosphate (as โ -0.004 โ known parameter range โ โ โ STREAM_010 โ โ P) - unspeโฆ โ โ failed (โฆ โ โ 2003 โ CAMCOR_020 โ Feb โ Temperature โ 58.0 โ known parameter range โ โ โ โ โ โ โ failed (โฆ โ โ 4813 โ DARGLE_030 โ Jan โ ortho-Phosphate (as โ 42.0 โ known parameter range โ โ โ โ โ P) - unspeโฆ โ โ failed (โฆ โ โ 4815 โ DARGLE_030 โ Feb โ ortho-Phosphate (as โ 22.0 โ known parameter range โ โ โ โ โ P) - unspeโฆ โ โ failed (โฆ โ โ 4857 โ DARGLE_030 โ Jul โ ortho-Phosphate (as โ 70.0 โ known parameter range โ โ โ โ โ P) - unspeโฆ โ โ failed (โฆ โ โ 4873 โ DARGLE_030 โ May โ ortho-Phosphate (as โ 29.0 โ known parameter range โ โ โ โ โ P) - unspeโฆ โ โ failed (โฆ โ โ 4893 โ DARGLE_030 โ Mar โ ortho-Phosphate (as โ 26.0 โ known parameter range โ โ โ โ โ P) - unspeโฆ โ โ failed (โฆ โ โ 4903 โ DARGLE_030 โ Sep โ ortho-Phosphate (as โ 25.0 โ known parameter range โ โ โ โ โ P) - unspeโฆ โ โ failed (โฆ โ โ 6096 โ GLENCREE_010 โ Feb โ ortho-Phosphate (as โ 27.0 โ known parameter range โ โ โ โ โ P) - unspeโฆ โ โ failed (โฆ โ โ 6117 โ GLENCREE_010 โ Jul โ ortho-Phosphate (as โ 27.0 โ known parameter range โ โ โ โ โ P) - unspeโฆ โ โ failed (โฆ โ โโโโโโโโโโโโโโดโโโโโโโโโโโโโโโดโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโ duplicate handling sample: These exact duplicates are handled deliberately before storage. shape: (10, 6) โโโโโโโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโฌโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ source_row โ location โ sample_date โ parameter_code โ value โ action โ โ --- โ --- โ --- โ --- โ --- โ --- โ โ i64 โ str โ str โ str โ f64 โ str โ โโโโโโโโโโโโโโชโโโโโโโโโโโชโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโโชโโโโโโโโชโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโก โ 3 โ ALLUA โ 2007-08-01 โ AMMONIA-TOTAL โ 0.033 โ skip exact duplicate before stโฆ โ โ 3 โ ALLUA โ 2007-08-01 โ BOD_-_5_DAYS โ 1.2 โ skip exact duplicate before stโฆ โ โ 3 โ ALLUA โ 2007-08-01 โ ORTHO-PHOSPHATE โ 0.019 โ skip exact duplicate before stโฆ โ โ 4 โ ALLUA โ 2007-08-01 โ AMMONIA-TOTAL โ 0.033 โ skip exact duplicate before stโฆ โ โ 4 โ ALLUA โ 2007-08-01 โ BOD_-_5_DAYS โ 1.2 โ skip exact duplicate before stโฆ โ โ 4 โ ALLUA โ 2007-08-01 โ ORTHO-PHOSPHATE โ 0.019 โ skip exact duplicate before stโฆ โ โ 4 โ ALLUA โ 2007-08-01 โ TEMPERATURE โ 17.8 โ skip exact duplicate before stโฆ โ โ 6 โ ALLUA โ 2007-09-01 โ ALKALINITY-TOTAL โ 19.0 โ skip exact duplicate before stโฆ โ โ 6 โ ALLUA โ 2007-09-01 โ BOD_-_5_DAYS โ 1.2 โ skip exact duplicate before stโฆ โ โ 6 โ ALLUA โ 2007-09-01 โ ORTHO-PHOSPHATE โ 0.019 โ skip exact duplicate before stโฆ โ โโโโโโโโโโโโโโดโโโโโโโโโโโดโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโดโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ trusted records sample: These records passed validation and are shaped for database insertion. shape: (5, 8) โโโโโโโโโโโโโโฌโโโโโโโโโโโโโฌโโโโโโโโโโโโโฌโโโโโโโโโโโโโฌโโโโโโโโโโโโโฌโโโโโโโโโโโโโฌโโโโโโโโฌโโโโโโโโโโโโโ โ source_row โ location โ sample_dat โ parameter โ parameter_ โ unit โ value โ source_col โ โ --- โ --- โ e โ --- โ code โ --- โ --- โ umn โ โ i64 โ str โ --- โ str โ --- โ str โ f64 โ --- โ โ โ โ str โ โ str โ โ โ str โ โโโโโโโโโโโโโโชโโโโโโโโโโโโโชโโโโโโโโโโโโโชโโโโโโโโโโโโโชโโโโโโโโโโโโโชโโโโโโโโโโโโโชโโโโโโโโชโโโโโโโโโโโโโก โ 1 โ ABBEYTOWN_ โ 2023-02-01 โ Alkalinity โ ALKALINITY โ as CaCO3 โ 314.0 โ Alkalinity โ โ โ 010 โ โ -total โ -TOTAL โ โ โ -total (as โ โ โ โ โ โ โ โ โ CaCO3) โ โ 1 โ ABBEYTOWN_ โ 2023-02-01 โ Ammonia-To โ AMMONIA-TO โ as N โ 0.033 โ Ammonia-To โ โ โ 010 โ โ tal โ TAL โ โ โ tal (as N) โ โ 1 โ ABBEYTOWN_ โ 2023-02-01 โ BOD - 5 โ BOD_-_5_DA โ Total โ 1.2 โ BOD - 5 โ โ โ 010 โ โ days โ YS โ โ โ days โ โ โ โ โ โ โ โ โ (Total) โ โ 1 โ ABBEYTOWN_ โ 2023-02-01 โ Chloride โ CHLORIDE โ not_encode โ 27.3 โ Chloride โ โ โ 010 โ โ โ โ d โ โ โ โ 1 โ ABBEYTOWN_ โ 2023-02-01 โ Conductivi โ CONDUCTIVI โ @25ยฐC โ 711.0 โ Conductivi โ โ โ 010 โ โ ty โ TY โ โ โ ty @25ยฐC โ โโโโโโโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโดโโโโโโโโโโโโโ storage readiness summary: This is the final count of clean records, rejected records, NULLs, and handled duplicates. shape: (6, 2) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโ โ metric โ value โ โ --- โ --- โ โ str โ i64 โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโก โ raw_measurement_rows โ 320749 โ โ trusted_records_ready_to_store โ 148360 โ โ records_rejected โ 20 โ โ explicit_null_values โ 0 โ โ exact_duplicates_removed โ 172369 โ โ repeated_measurements_kept_witโฆ โ 31854 โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโ principle: This is the rule for deciding what is safe to store. shape: (1, 1) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ principle โ โ --- โ โ str โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโก โ The database should store cleaโฆ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
7. Store clean data with structure
use data_pipeline::quality_flow::store_clean_data_with_structure;
use polars::{
error::PolarsResult,
io::{
SerReader,
csv::read::{CsvParseOptions, CsvReadOptions},
},
};
fn main() -> PolarsResult<()> {
let df = CsvReadOptions::default()
.with_has_header(true)
// Discovery step: scan the file because we do not know columns yet.
.with_infer_schema_length(Some(10_000))
.with_parse_options(CsvParseOptions::default().with_try_parse_dates(true))
.try_into_reader_with_file_path(Some(
"data/Water Quality Monitoring Dataset_ Ireland.csv".into(),
))?
.finish()?;
// 7. Store clean data with structure
store_clean_data_with_structure(df)?;
Ok(())
} cargo run --bin store_clean_data_with_structure============================================================ 7. Store clean data with structure ============================================================ database file: This is the SQLite file written for the API project. shape: (1, 2) โโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ item โ value โ โ --- โ --- โ โ str โ str โ โโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโก โ sqlite_file โ /Users/chiefkemist/Documents/nโฆ โ โโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ structured schema: The cleaned data is stored across small tables instead of one giant messy table. shape: (5, 2) โโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ table โ purpose โ โ --- โ --- โ โ str โ str โ โโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโก โ locations โ one row per normalized locatioโฆ โ โ parameters โ one row per normalized parametโฆ โ โ measurements โ trusted observed measurements โ โ ingestion_runs โ source file, import time, counโฆ โ โ rejected_rows โ rows that failed validation orโฆ โ โโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ ingestion run summary: This records where the data came from and what happened during import. shape: (6, 2) โโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโ โ metric โ value โ โ --- โ --- โ โ str โ i64 โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโก โ ingestion_run_id โ 1 โ โ raw_rows โ 29159 โ โ raw_measurement_rows โ 320749 โ โ accepted_measurements โ 148360 โ โ rejected_rows โ 20 โ โ exact_duplicates_removed โ 172369 โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโ database table counts: These counts are read back from SQLite after the write finishes. shape: (5, 2) โโโโโโโโโโโโโโโโโโฌโโโโโโโโโ โ table โ rows โ โ --- โ --- โ โ str โ i64 โ โโโโโโโโโโโโโโโโโโชโโโโโโโโโก โ ingestion_runs โ 1 โ โ locations โ 160 โ โ parameters โ 11 โ โ measurements โ 148360 โ โ rejected_rows โ 20 โ โโโโโโโโโโโโโโโโโโดโโโโโโโโโ stored measurement sample: These accepted rows are stored in the measurements table with foreign keys. shape: (5, 6) โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโฌโโโโโโโโ โ source_row โ location โ sample_date โ parameter_code โ unit โ value โ โ --- โ --- โ --- โ --- โ --- โ --- โ โ i64 โ str โ str โ str โ str โ f64 โ โโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโชโโโโโโโโก โ 1 โ ABBEYTOWN_010 โ 2023-02-01 โ ALKALINITY-TOTAL โ as CaCO3 โ 314.0 โ โ 1 โ ABBEYTOWN_010 โ 2023-02-01 โ AMMONIA-TOTAL โ as N โ 0.033 โ โ 1 โ ABBEYTOWN_010 โ 2023-02-01 โ BOD_-_5_DAYS โ Total โ 1.2 โ โ 1 โ ABBEYTOWN_010 โ 2023-02-01 โ CHLORIDE โ not_encoded โ 27.3 โ โ 1 โ ABBEYTOWN_010 โ 2023-02-01 โ CONDUCTIVITY โ @25ยฐC โ 711.0 โ โโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโดโโโโโโโโ rejected row sample: These failed rows are stored separately for traceability. shape: (5, 6) โโโโโโโโโโโโโโฌโโโโโโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโ โ source_row โ location โ raw_date โ source_column โ raw_value โ rejection_reason โ โ --- โ --- โ --- โ --- โ --- โ --- โ โ i64 โ str โ str โ str โ str โ str โ โโโโโโโโโโโโโโชโโโโโโโโโโโโโชโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโโโโโโโโก โ 111 โ ASKANAGAP โ Jan โ ortho-Phosphate (as P) โ -0.004 โ known parameter range โ โ โ STREAM_010 โ โ - unspeโฆ โ โ failed (โฆ โ โ 2003 โ CAMCOR_020 โ Feb โ Temperature โ 58.0 โ known parameter range โ โ โ โ โ โ โ failed (โฆ โ โ 4813 โ DARGLE_030 โ Jan โ ortho-Phosphate (as P) โ 42.0 โ known parameter range โ โ โ โ โ - unspeโฆ โ โ failed (โฆ โ โ 4815 โ DARGLE_030 โ Feb โ ortho-Phosphate (as P) โ 22.0 โ known parameter range โ โ โ โ โ - unspeโฆ โ โ failed (โฆ โ โ 4857 โ DARGLE_030 โ Jul โ ortho-Phosphate (as P) โ 70.0 โ known parameter range โ โ โ โ โ - unspeโฆ โ โ failed (โฆ โ โโโโโโโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโ principle: This is the reason for storing accepted rows, rejected rows, and ingestion metadata. shape: (1, 1) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ principle โ โ --- โ โ str โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโก โ The database is part of the daโฆ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
8. Visualize after cleaning
use data_pipeline::quality_flow::visualize_after_cleaning;
use polars::{
error::PolarsResult,
io::{
SerReader,
csv::read::{CsvParseOptions, CsvReadOptions},
},
};
fn main() -> PolarsResult<()> {
let df = CsvReadOptions::default()
.with_has_header(true)
// Discovery step: scan the file because we do not know columns yet.
.with_infer_schema_length(Some(10_000))
.with_parse_options(CsvParseOptions::default().with_try_parse_dates(true))
.try_into_reader_with_file_path(Some(
"data/Water Quality Monitoring Dataset_ Ireland.csv".into(),
))?
.finish()?;
// 8. Visualize after cleaning
visualize_after_cleaning(df)?;
Ok(())
} cargo run --bin visualize_after_cleaning============================================================ 8. Visualize after cleaning ============================================================ dashboard handoff: The dashboard reads the cleaned SQLite database produced by the storage step. shape: (5, 2) โโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ item โ value โ โ --- โ --- โ โ str โ str โ โโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโก โ raw_rows_available โ 29159 โ โ sqlite_file โ /Users/chiefkemist/Documents/nโฆ โ โ dashboard_page โ http://localhost:3434/data_viz โ โ summary_json โ http://localhost:3434/api/dashโฆ โ โ timeseries_json โ http://localhost:3434/api/dashโฆ โ โโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ dashboard views: These views turn the cleaned records into visual checks for patterns, gaps, and problems. shape: (9, 2) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ view โ source โ โ --- โ --- โ โ str โ str โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโชโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโก โ pH over time by location โ measurements joined with locatโฆ โ โ temperature over time โ measurements joined with locatโฆ โ โ dissolved oxygen over time โ measurements joined with locatโฆ โ โ ammonia spikes by location โ measurements joined with locatโฆ โ โ missing-data heatmap โ measurement coverage by locatiโฆ โ โ outlier count by parameter โ rejected_rows grouped by sourcโฆ โ โ data completeness by location โ measurements grouped by locatiโฆ โ โ before/after cleaning summary โ ingestion_runs accepted and reโฆ โ โ water-quality score by locatioโฆ โ aggregated pH, dissolved oxygeโฆ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ principle: Visualization is the final check that the pipeline produced useful data. shape: (1, 1) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ principle โ โ --- โ โ str โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโก โ Understand, clean, validate, sโฆ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ

