Quarto Demo with SAS

A simple demo of using SAS in Quarto. In this document we demonstrate the following:

Setup

saspy is required to run SAS commands from python/jupyter.

import saspy
import pandas as pd

sas = saspy.SASsession()
Using SAS Config named: oda
SAS Connection established. Subprocess id is 15284

Running SAS code directly from the notebook

The sas.submit() method can be used to submit SAS code directly. The method returns a dictionary with two keys, LOG and LST. The LOG can simply be printed and the LST rendered as HTML using sas.HTML().

ll = sas.submit("""
libname work list;

proc sql;
   select type, count(*) as 'number of models'n, avg(MPG_city) as 'Avg(MPG_City)'n
   from sashelp.cars
   group by type
   order by 3 desc;
quit; 
""")

keys

list of dictionary keys returned by SAS

ll.keys()
dict_keys(['LOG', 'LST'])

LST

LST output

sas.HTML(ll['LST'])
SAS Output

The SAS System

Type number of models Avg(MPG_City)
Hybrid 3 55
Wagon 30 21.1
Sedan 262 21.08397
Sports 49 18.40816
Truck 24 16.5
SUV 60 16.1

Alternatively, the LST can be rendered automatically using the sas.submitLST() method

ll = sas.submitLST("""
libname work list;

proc sql;
   select type, count(*) as 'number of models'n, avg(MPG_city) as 'Avg(MPG_City)'n
   from sashelp.cars
   group by type
   order by 3 desc;
quit; 
""")
SAS Output

The SAS System

Type number of models Avg(MPG_City)
Hybrid 3 55
Wagon 30 21.1
Sedan 262 21.08397
Sports 49 18.40816
Truck 24 16.5
SUV 60 16.1

Working with SAS and python together

Importing data from SAS

data_sas = sas.sasdata('cars', 'sashelp')

Running SAS PROC MEANS from Quarto

data_sas.means()
Variable Label N NMiss Median Mean StdDev Min P25 P50 P75 Max
0 MSRP NaN 428.0 0.0 27635.0 32774.855140 19431.716674 10280.0 20329.50 27635.0 39215.0 192465.0
1 Invoice NaN 428.0 0.0 25294.5 30014.700935 17642.117750 9875.0 18851.00 25294.5 35732.5 173560.0
2 EngineSize Engine Size (L) 428.0 0.0 3.0 3.196729 1.108595 1.3 2.35 3.0 3.9 8.3
3 Cylinders NaN 426.0 2.0 6.0 5.807512 1.558443 3.0 4.00 6.0 6.0 12.0
4 Horsepower NaN 428.0 0.0 210.0 215.885514 71.836032 73.0 165.00 210.0 255.0 500.0
5 MPG_City MPG (City) 428.0 0.0 19.0 20.060748 5.238218 10.0 17.00 19.0 21.5 60.0
6 MPG_Highway MPG (Highway) 428.0 0.0 26.0 26.843458 5.741201 12.0 24.00 26.0 29.0 66.0
7 Weight Weight (LBS) 428.0 0.0 3474.5 3577.953271 758.983215 1850.0 3103.00 3474.5 3978.5 7190.0
8 Wheelbase Wheelbase (IN) 428.0 0.0 107.0 108.154206 8.311813 89.0 103.00 107.0 112.0 144.0
9 Length Length (IN) 428.0 0.0 187.0 186.362150 14.357991 143.0 178.00 187.0 194.0 238.0

A simple bar chart (from SAS)

data_sas.bar('EngineSize')
SAS Output
The SGPlot Procedure

Converting the data to a Pandas DataFrame

data_pd = data_sas.to_df()
data_pd.describe()
MSRP Invoice EngineSize Cylinders Horsepower MPG_City MPG_Highway Weight Wheelbase Length
count 428.000000 428.000000 428.000000 426.000000 428.000000 428.000000 428.000000 428.000000 428.000000 428.000000
mean 32774.855140 30014.700935 3.196729 5.807512 215.885514 20.060748 26.843458 3577.953271 108.154206 186.362150
std 19431.716674 17642.117750 1.108595 1.558443 71.836032 5.238218 5.741201 758.983215 8.311813 14.357991
min 10280.000000 9875.000000 1.300000 3.000000 73.000000 10.000000 12.000000 1850.000000 89.000000 143.000000
25% 20334.250000 18866.000000 2.375000 4.000000 165.000000 17.000000 24.000000 3104.000000 103.000000 178.000000
50% 27635.000000 25294.500000 3.000000 6.000000 210.000000 19.000000 26.000000 3474.500000 107.000000 187.000000
75% 39205.000000 35710.250000 3.900000 6.000000 255.000000 21.250000 29.000000 3977.750000 112.000000 194.000000
max 192465.000000 173560.000000 8.300000 12.000000 500.000000 60.000000 66.000000 7190.000000 144.000000 238.000000

Interactive visualization with observable

SAS -> python -> observable
Now that our data is available in python we can use the ojs_define function to make it available to observable.

# make data available to observable
ojs_define(ojs_data = data_pd)

The observabale code to generate the plot below is included as folded code.

Code
viewof MPG_City = Inputs.range(
  [10, 60], 
  {value: 10, step: 1, label: "MPG City"}
)

// filter data based on the value based on the MPG_City input
filtered_data = transpose(ojs_data).filter(function(car) {
  return MPG_City < car.MPG_City;
})
Code
plt = Plot.plot({
    marks: [
        Plot.dot(filtered_data, {x: "EngineSize", y: "Horsepower"})
    ]
})