Start here →
DATA step, PROC SQL, and macros parsed structurally. Emitted as set-based PySpark you can run on Databricks, EMR, or your own cluster.
Deterministic parsers read the SAS estate and emit native PySpark code — not a Python while-loop that still reads one row.
SAS programs → SAS2PY parser → Native PySpark
SAS2PY Parser
Deterministic parse
AI optional
AI is an optional add-on, off by default — the conversion runs end to end without it, air-gapped if your estate requires it.
A for-loop over a SAS dataset is the same program in a new costume. We emit joins, windows, and aggregations.
Databricks, EMR, or on-prem Spark — the parser does not care. The notebook is portable.
Expanded before emit. The output is Python a Spark engineer can read.
A row-at-a-time running total — emitted as a filter and window, not a Python for-loop.
/* SAS row loop */ data gold; set txn; if amount > 1000; tot + amount; run;
# loop → filter + window
from pyspark.sql import functions as F
from pyspark.sql.window import Window
gold = txn.filter(F.col("amount") > 1000).withColumn(
"tot", F.sum("amount").over(Window.orderBy("txn_date"))
)
The running total is a window. The filter is a predicate. No iterator.
| SAS | PySpark | Notes |
|---|---|---|
| DATA step | DataFrame API | No row loops |
| PROC SQL | spark.sql | Same joins |
| PROC MEANS / SUMMARY | groupBy + agg | Native |
| Macro | Expanded Python | Readable |
| SAS dataset | Parquet / Delta / Iceberg | Your choice |
SAS output compared to PySpark output — row by row, column by column. Differences flagged before sign-off.
See how Data Matching works →Grid and DI Studio jobs rewritten as Apache PySpark on EMR and Glue. Same S3 prefixes. No lakehouse control plane required.
Read the case study →