import saspy
import pandas as pd
= saspy.SASsession() sas
Using SAS Config named: oda
SAS Connection established. Subprocess id is 15284
A simple demo of using SAS in Quarto. In this document we demonstrate the following:
saspy is required to run SAS commands from python/jupyter.
import saspy
import pandas as pd
= saspy.SASsession() sas
Using SAS Config named: oda
SAS Connection established. Subprocess id is 15284
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()
.
= sas.submit("""
ll 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;
""")
list of dictionary keys returned by SAS
ll.keys()
dict_keys(['LOG', 'LST'])
LST output
'LST']) sas.HTML(ll[
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
= sas.submitLST("""
ll 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;
""")
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 |
= sas.sasdata('cars', 'sashelp') data_sas
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 |
'EngineSize') data_sas.bar(
= data_sas.to_df()
data_pd 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 |
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
= data_pd) ojs_define(ojs_data
The observabale code to generate the plot below is included as folded code.
= Inputs.range(
viewof MPG_City 10, 60],
[value: 10, step: 1, label: "MPG City"}
{
)
// filter data based on the value based on the MPG_City input
= transpose(ojs_data).filter(function(car) {
filtered_data return MPG_City < car.MPG_City;
})
= Plot.plot({
plt marks: [
.dot(filtered_data, {x: "EngineSize", y: "Horsepower"})
Plot
] })