What you will be able to do
- Explain why a Snowpark DataFrame is a lazy query and how to build one from a table, a stage or a SQL statement
- Choose between open-source preprocessors, Snowflake ML preprocessors and Ray map_batches based on data scale and where compute runs
- Build a Snowflake ML preprocessing Pipeline that fits and transforms a large table inside Snowflake
- Decide when to write a transformation as a SQL string, as Snowpark constructs or as a UDF, and when Snowpark code needs a Snowpark-optimized warehouse
- Analyze a feature engineering use case to decide whether to author it as SQL or as Snowpark, knowing that both execute in Snowflake
Key concept
Pushdown — Snowpark and Snowflake ML preprocessors don't bring rows to your client. They turn your Python into work that runs inside Snowflake, next to the data, on Snowflake-managed compute. Distributed feature engineering in Snowflake depends on that.
1.Snowpark DataFrames are lazy queries
In Snowpark, the DataFrame is the main way to query and process data, so every feature pipeline here starts with one. Its key property is laziness. A Snowpark DataFrame does not hold rows. It describes a relational dataset, much like a query that has not been run yet. Snowpark libraries exist for Java, Python and Scala, and the examples below use Python.
Working with a DataFrame takes three steps. First you construct it from a data source using the Session. session.table() reads a table, view or stream. session.create_dataframe() takes local values. session.range() generates a sequence. session.read followed by a format method such as json or csv reads files in a stage. session.sql() wraps the result of a SQL statement. Second, you specify transformations: which columns to select, how to filter rows, and how to sort and group. Third, you call an action such as collect(). Only the action sends the generated SQL to Snowflake for execution.
>>> # Create a DataFrame with the "id" and "name" columns from the "sample_product_data" table.
>>> # This does not execute the query.
>>> df = session.table("sample_product_data").select(col("id"), col("name"))
>>> # Send the query to the server for execution and
>>> # return a list of Rows containing the results.
>>> results = df.collect()Lazy evaluation matters for feature engineering. Because nothing runs until the end, Snowpark can defer transformations and batch many of them into a single operation. That cuts the data moved between your client and Snowflake and improves performance. And because the work is pushed down, you need no separate cluster: Snowflake runs the computation and manages scale.
This laziness is also the starting point for analyzing a use case as SQL versus Snowpark. A DataFrame is a query that sends SQL to Snowflake when an action runs, so a SQL statement and a Snowpark DataFrame are not rival engines. Both execute in Snowflake, next to the data. Your analysis is about authoring, not about where the compute runs. The section "Choosing between SQL and Snowpark" below works through the authoring options.
Checkpoint 1 of 7· Put it in order
Put the steps for getting data into a Snowpark DataFrame in order
- 1.Construct the DataFrame from a source such as a table, a staged file or a SQL statement
- 2.Invoke an action such as collect() so the statement runs
- 3.Specify the transformations: selected columns, row filters, sorting and grouping
The docs give this exact sequence. Construction and transformations only describe the query, and the action is what executes it.
“you must invoke a method that performs an action (for example, the collect() method)”Source: docs.snowflake.com
2.Three ways to engineer features in Snowflake ML
After the data is in a DataFrame, you choose how to transform it into features. Snowflake ML documents three approaches. It tells you to pick based on data size, performance requirements and how much custom transformation logic you need. The main difference between them is where the compute runs.
| Aspect | OSS (including scikit-learn) | Snowflake ML preprocessors | Ray map_batches |
|---|---|---|---|
| Scale | Small & medium datasets | Large/distributed data | Large/distributed data |
| Execution environment | In memory | Pushdown to the default warehouse that you’re using to run SQL queries | Across nodes in a compute pool |
| Compute resources | Snowpark Container Services (Compute Pool) | Warehouse | Snowpark Container Services (Compute Pool) |
| Performance | Fast for local, in-memory workloads; scale limited and non-distributed | Designed for scalable, distributed feature engineering | Highly parallel and resource-managed, excels on large/unstructured data |
| Use case suitability | Quickly prototyping and experimentation | Production workflows with large datasets | Large data workflows that require custom resource controls |
Open-source preprocessors such as scikit-learn run in memory on a single node within Container Runtime. They are quick for prototyping, but they don't distribute. Read the table's two OSS cells together: "in memory" describes how the data is processed, and "Snowpark Container Services (Compute Pool)" names the compute that hosts the Container Runtime node doing that work. Neither is a warehouse. Only the Snowflake ML preprocessors list a warehouse as their compute. They are the warehouse-native option: their processing is distributed across warehouse compute resources. Ray map_batches runs custom functions in parallel across the nodes of a compute pool. Its execution is also lazy: nothing is processed until you materialize the dataset, which reduces memory use. Ray is the choice for large-scale processing with custom logic, especially on unstructured data.
Checkpoint 2 of 7· Match them up
Match each approach to where its processing runs
Tap a term, then the definition that fits it.
Only the Snowflake ML preprocessors run on a warehouse. The OSS libraries run in memory on one node, and Ray runs across the nodes of a compute pool.
“These APIs distribute the processing across warehouse compute resources.”Source: docs.snowflake.com
Sources3
3.Snowflake ML preprocessors and Pipelines
The Snowflake ML preprocessors are in snowflake.ml.modeling.preprocessing, and the Pipeline class that chains them is in snowflake.ml.modeling.pipeline. They look like scikit-learn, with two important differences. First, each preprocessor names its columns with input_cols and output_cols rather than receiving a column selection through a ColumnTransformer. Second, the input is a Snowpark DataFrame, usually from session.table(), not a pandas DataFrame held in memory. When you call fit_transform on the pipeline, the fit and the transform run in Snowflake as distributed work on the warehouse.
# Load your data from a Snowflake table as a DataFrame
df = session.table('CUSTOMER_DATA')
# Define Snowflake ML preprocessors
scaler = StandardScaler(input_cols=['AGE', 'INCOME'], output_cols=['AGE_SCALED', 'INCOME_SCALED'])
encoder = OneHotEncoder(input_cols=['CITY'], output_cols=['CITY_ENCODED'])
pipeline = Pipeline(steps=[
('scaling', scaler),
('encoding', encoder)
])
# Fit and transform data in Snowflake (distributed)
result = pipeline.fit_transform(df)
result.show()The fit step is what makes a scaler stateful. A min-max scaler, for example, learns the minimum and maximum values of each column from the training sample, and that learned state is what the transform step applies. The Snowflake guide describes such transformations as capturing the global state of the training sample, which is why they belong with the model rather than being recomputed on whatever data arrives later.
Checkpoint 3 of 7· Fill the gap
Which module completes this import of the Snowflake ML preprocessors?
from snowflake.snowpark import Session
from snowflake.ml.modeling. ? import StandardScaler, OneHotEncoder
from snowflake.ml.modeling.pipeline import PipelineThe scalers and encoders are in snowflake.ml.modeling.preprocessing. The Pipeline class is in the separate snowflake.ml.modeling.pipeline module.
Source: docs.snowflake.comAnswer to the reflection: refitting on the inference batch would learn new statistics, such as a different minimum and maximum, from that batch alone. Its features would then be scaled on a different basis from the one the model was trained on. Reuse the state captured from the training sample instead.
Checkpoint 4 of 7· Exam question
A team stores 2 billion customer rows in a Snowflake table. A data scientist calls `fit` on a `snowflake.ml.modeling.preprocessing.MinMaxScaler` using a Snowpark DataFrame built from that table. Where are the per-column minimum and maximum values computed?
Correct answer: B — Inside the active virtual warehouse, which evaluates generated SQL aggregates over the table so rows never leave Snowflake
- A. Incorrect: no container service is provisioned automatically, and the statistics are exact over the full table rather than estimated from a sample.
- B. Correct: with a Snowpark DataFrame input, the fit step is translated into SQL aggregate queries that run on the warehouse, so the data is processed where it lives.
- C. Incorrect: the library does not unload the table into staged Parquet files to compute statistics; it pushes the computation down as queries.
- D. Incorrect: row-by-row transfer to the client is what happens with local pandas workflows, not with Snowflake ML preprocessors on Snowpark DataFrames.
4.Choosing between SQL and Snowpark
"SQL or Snowpark" is less of a choice than it sounds. A Snowpark DataFrame evaluates by sending SQL statements to Snowflake, so both run on the same engine. The real decision is how you author the logic and where any custom code runs.
Set-based logic you already have as SQL. session.sql() accepts a SQL string, and it can run SELECT statements over tables and staged files. The native Snowpark constructs, such as select and col, are still the better authoring option. They give intelligent code completion and type checking. For reading data, the docs also say session.table() and session.read give better syntax and error highlighting than embedding the same query in a string.
Logic that SQL expresses poorly. For loops or batch functions, write a user-defined function. Snowpark sends the UDF code to the server, and Snowflake runs it in parallel next to the data, so you don't pull rows to the client.
Python code that needs a lot of memory. Snowpark code runs on standard warehouses too. Snowpark-optimized warehouses are recommended for Snowpark workloads with large memory requirements or a dependency on a specific CPU architecture, such as ML training in a stored procedure on a single warehouse node. By default they provide 16x the memory per node of a standard warehouse. The resource_constraint property of CREATE or ALTER WAREHOUSE configures memory and CPU architecture. Creating or resuming one can take longer than a standard warehouse.
These sources give no formal SQL-versus-Snowpark decision matrix. The guidance above is what they do support.
Checkpoint 5 of 7· Check yourself
A team needs a per-row transformation written as custom Python logic, and they must not pull the data to the client. Which approach fits?
Custom codeful logic is what UDFs are for: Snowflake parallelizes it at scale next to the data. SQL strings and native constructs author queries, and collect() brings the data to the client.
“creating as a UDF will allow Snowflake to parallelize and apply the codeful logic at scale within Snowflake”Source: docs.snowflake.com
| Memory (up to) | RESOURCE_CONSTRAINT values | Minimum warehouse size |
|---|---|---|
| 16GB | MEMORY_1X, MEMORY_1X_x86 | XSMALL |
| 256GB | MEMORY_16X, MEMORY_16X_x86 | M |
| 1TB | MEMORY_64X, MEMORY_64X_x86 | L |
Checkpoint 6 of 7· Check yourself
Which workload is Snowflake's recommendation for a Snowpark-optimized warehouse aimed at?
The recommendation targets Snowpark workloads with large memory needs or CPU-architecture dependencies. These warehouses can also be slower to create and resume than standard warehouses.
“recommended for running Snowpark workloads such as code that has large memory requirements or dependencies on a specific CPU architecture”Source: docs.snowflake.com
Checkpoint 7 of 7· Exam question
A fraud model was trained with a `snowflake.ml.modeling.preprocessing.OneHotEncoder` fitted on the `merchant_category` column. During batch inference the encoder raises an error because new merchant categories appeared that were absent from the training data. Which change allows scoring to continue without refitting?
Correct answer: D — Construct the encoder with `handle_unknown="ignore"` so that unseen categories are encoded as all-zero indicator vectors
- A. Incorrect: `drop_input_cols` only controls whether the source columns remain in the output; it does not alter how unknown categories are handled.
- B. Incorrect: refitting on inference data changes the encoded column set, which breaks the feature layout the trained model expects (training/serving skew).
- C. Incorrect: a MinMaxScaler works on numeric data and cannot map arbitrary strings, so it does not address unseen categories.
- D. Correct: `handle_unknown="ignore"` makes unseen categories produce all-zero encodings instead of raising, keeping the learned vocabulary and column layout stable for the model.
Exam traps
Each one states something that sounds right. Open it to see what is actually true.
1.Snowflake ML preprocessors work like scikit-learn: they pull the table into client memory and transform it there.Why is that wrong?
They are pushed down. The fit and transform run as distributed work on the warehouse, which is why they suit large datasets and production workloads.
Covered in Snowflake ML preprocessors and Pipelines
2.A Snowpark-optimized warehouse is a general upgrade that speeds up any query.Why is that wrong?
It targets Snowpark code with large memory or specific CPU-architecture needs. The extra memory per node solves memory pressure in that code, not general SQL performance.
Covered in Choosing between SQL and Snowpark
3.Choosing SQL over Snowpark (or the reverse) decides whether the work runs inside Snowflake.Why is that wrong?
Both do. A Snowpark DataFrame sends SQL statements to Snowflake when an action runs, so the choice is about how you author the logic, not where it executes.
Covered in Snowpark DataFrames are lazy queries
Sources
Every claim above is drawn from one of these pages, quoted as it was written on the date shown.
- 1.
“In a sense, a DataFrame is like a query that needs to be evaluated in order to retrieve data.”
↩︎ Snowpark DataFrames are lazy queries“using the table method and read property offer better syntax highlighting, error highlighting, and intelligent code completion in development tools”
↩︎ Choosing between SQL and Snowpark“A DataFrame represents a relational dataset that is evaluated lazily: it only executes when a specific action is triggered.”
↩︎ Prediction“you must invoke a method that performs an action (for example, the collect() method)”
↩︎ Checkpoint - 2.
“delay running data transformation until as late in the pipeline as possible while batching up many operations into a single operation”
↩︎ Snowpark DataFrames are lazy queries“No requirement for a separate cluster outside of Snowflake for computations.”
↩︎ Snowpark DataFrames are lazy queries“you benefit from features like intelligent code completion and type checking when you use the native language constructs provided by Snowpark”
↩︎ Choosing between SQL and Snowpark“creating as a UDF will allow Snowflake to parallelize and apply the codeful logic at scale within Snowflake”
↩︎ Choosing between SQL and Snowpark“Snowpark pushes down all data transformation and heavy lifting to the Snowflake data cloud”
↩︎ Key concept“sends the corresponding SQL statements to the Snowflake database for execution”
↩︎ Exam trap 3 - 3.
“Choose the approach that best matches your data size, performance requirements, and need for custom transformation logic.”
↩︎ Three ways to engineer features in Snowflake ML“This approach is ideal for large-scale data processing with custom logic”
↩︎ Three ways to engineer features in Snowflake ML“use familiar Python ML libraries that run locally or on single nodes within Container Runtime”
↩︎ Three ways to engineer features in Snowflake ML“The Snowflake ML preprocessors are a subset of the preprocessors available in scikit-learn, but they cover the most common use cases.”
↩︎ Snowflake ML preprocessors and Pipelines“These preprocessors are pushed down to scale across warehouses.”
↩︎ Exam trap 1“These APIs distribute the processing across warehouse compute resources.”
↩︎ Checkpoint - 4.
“Leverage distributed preprocessing functions in snowflake.ml.modeling.preprocessing.”
↩︎ Snowflake ML preprocessors and Pipelines - 5.
“The Snowflake ML API reference includes documentation on all publicly-released functionality.”
↩︎ Snowflake ML preprocessors and Pipelines - 6.https://www.snowflake.com/en/developers/guides/advanced-guide-to-snowflake-feature-storeSecondary source
“they capture the global state (e.g. minimum and maximum values for columns) of our training sample”
↩︎ Snowflake ML preprocessors and Pipelines - 7.
“Example workloads include Machine Learning (ML) training use cases using a stored procedure on a single virtual warehouse node.”
↩︎ Choosing between SQL and Snowpark“The default configuration for a Snowpark-optimized warehouse provides 16x memory per node compared to a standard warehouse.”
↩︎ Exam trap 2“recommended for running Snowpark workloads such as code that has large memory requirements or dependencies on a specific CPU architecture”
↩︎ Checkpoint