Running SAS in Quarto. Part 2

Running SAS in Quarto. Part 2
Quarto
Python
Author

Harvey

Published

July 2, 2022

An earlier post demonstrated how the jupyter SAS kernel could be used to run SAS from within a Quarto document. This post demonstrates the saspy library and how SAS can be incorporated into Quarto, ultimately passing data to an interactive observable visualization.

Setup and Configure

saspy needs to be installed and configured. See the previous post for details on configuring sascfg_personal.py and _authinfo files.

Quarto qmd Document

The saspy library can be used to communicate between python and sas. It works within Quarto python chunks without requiring the SAS kernel. Below is an example document which demonstrates how to call SAS code directly within a python chunk, how to execute SAS commands from within a python chunk and how to pass data from SAS to python. Once data have been passed from SAS to python we have the opportunity of using observable to render interactive visualizations.

---
title: "Quarto Demo with SAS"
format: html
self-contained: true
---

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

-  Running SAS code directly
-  Importing a SAS dataset
-  Executing SAS commands using saspy
-  Converting SAS dataset to a Pandas DataFrame
-  Interactive visualization using observable 

# Setup

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

```{python}
import saspy
import pandas as pd

sas = saspy.SASsession()
```

# 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()`.

```{python}
#| echo: true
#| results: asis
#| warning: false

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
```{python}
ll.keys()
```

### LST
LST output
```{python}
sas.HTML(ll['LST'])
```

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

```{python}
#| echo: true
#| results: asis
#| warning: false

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; 
""")

```

# Working with SAS and python together

## Importing data from SAS
```{python}
data_sas = sas.sasdata('cars', 'sashelp')
```

## Running SAS PROC MEANS from Quarto
```{python}
data_sas.means()
```

## A simple bar chart (from SAS)
```{python}
data_sas.bar('EngineSize')
```

## Converting the data to a Pandas DataFrame
```{python}
data_pd = data_sas.to_df()
data_pd.describe()
```

# 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.

```{python}
#| warning: false
# make data available to observable
ojs_define(ojs_data = data_pd)
```

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

```{ojs}
//| code-fold: true
// create an interactive input, limiting MPG_City
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;
})
```


```{ojs}
//| code-fold: true
// build a dotplot from the filtered data
plt = Plot.plot({
    marks: [
        Plot.dot(filtered_data, {x: "EngineSize", y: "Horsepower"})
    ]
})
```

Output