1.5. SAS PROCEDURES -> Equivalent in R
1.5.7. SAS PROC PRINT and R Equivalents
This document walks through the core features of PROC PRINT in SAS and how to achieve the same behaviors in R—first covering the basics, then exploring advanced reporting tricks. Each section contains:
- Concept: what you’re trying to do
- SAS Code vs. R Code
- Detailed Explanation of how each code works
- Input Snapshot
- Expected Output
- Workflow: how it works
1. Print the Entire Data Set
**Concept**
List every column and every row in your data.**SAS Code**
proc print data=sales_data;
run;
SAS Code Explanation
proc printinvokes the PRINT procedure.data=sales_datatells SAS which table to read (by default from the WORK library).- Without any
varor subsetting options, PROC PRINT loops over all variables and all observations. - It sends the output to the open ODS destination (e.g., the listing or HTML).
run;finalizes and executes the step.
**R Code (base)**
# Dummy data for execution
library(dplyr)
sales_data <- tibble::tibble(
Region = c("North", "North", "South", "South", "East", "East"),
Month = c("Jan", "Feb", "Jan", "Feb", "Jan", "Feb"),
Sales = c(1200, 1500, 900, 1100, 1300, 1400),
Profit = c(200, 250, 100, 150, 300, 350),
Category = c("A", "B", "A", "B", "A", "B")
)
sales_data # or print(sales_data)
R Code Explanation
- Typing the name of a
data.frameortibblein the console implicitly callsprint(). - For a base
data.frame,print()displays every row and column (within console width). - For a
tibble(from tibble/dplyr), printing shows the first 10 rows and all columns, plus a note about remaining rows/columns.
Input (sales_data)
| Region | Month | Sales | Profit | Category |
|---|---|---|---|---|
| North | Jan | 1200 | 200 | A |
| North | Feb | 1500 | 250 | B |
| South | Jan | 900 | 100 | A |
| South | Feb | 1100 | 150 | B |
| East | Jan | 1300 | 300 | A |
| East | Feb | 1400 | 350 | B |
Expected Output
All six rows and five columns are rendered.
Workflow
- SAS reads
sales_data, streams records to PROC PRINT, and renders them. - R evaluates the object, invokes
print(), and displays the table.
2. Print Specific Variables
**Concept**
Show only the columns you care about.**SAS Code**
proc print data=sales_data;
var Region Sales;
run;
SAS Code Explanation
- The
varstatement restricts PROC PRINT’s output to the listed variables (RegionandSales). - SAS builds an internal view of only those columns before printing.
- Unlisted variables are completely omitted from the report.
**R Code (dplyr)**
library(dplyr)
sales_data %>%
select(Region, Sales)
R Code Explanation
select(Region, Sales)constructs a new tibble containing only those two columns.- The pipe (
%>%) passes the originalsales_dataintoselect(). - Because the last call returns a tibble, the console prints it automatically.
Input (same as above)
Expected Output
| Region | Sales |
|---|---|
| North | 1200 |
| North | 1500 |
| South | 900 |
| South | 1100 |
| East | 1300 |
| East | 1400 |
Workflow
- SAS’s VAR selection happens inside PROC PRINT.
- R’s
select()removes unwanted columns before rendering.
3. Print the First N Observations
**Concept**
Limit output to the top *N* rows.**SAS Code**
proc print data=sales_data(obs=3);
var Region Sales;
run;
SAS Code Explanation
- The data set option
(obs=3)tells SAS to stop reading after the third observation. - Combined with
var, only columnsRegionandSalesfor the first three rows are printed. - Internally, SAS applies that row-limit filter during data set input.
**R Code (dplyr + head)**
sales_data %>%
select(Region, Sales) %>%
head(3)
R Code Explanation
select(Region, Sales)first reduces columns.head(3)then returns only the first three rows of that result.- The console prints the returned tibble.
Expected Output
| Region | Sales |
|---|---|
| North | 1200 |
| North | 1500 |
| South | 900 |
Workflow
- SAS stops after row 3, then prints.
- R chains column selection then row filtering.
4. Print the Last N Observations
**Concept**
Show the tail end of the table.**SAS Code**
> **Note:** PROC PRINT has no built-in TAIL. You can use PROC SQL or a DATA step to filter by row number.proc sql;
select * from
(select *, monotonic() as _n_ from sales_data)
having _n_ > (nobs-3);
quit;
SAS Code Explanation
monotonic()generates a row counter_n_.nobsis a macro or automatic variable representing total rows.- The
havingclause keeps only those rows where_n_is within the last three. - PROC SQL then prints the result.
**R Code (dplyr + tail)**
sales_data %>%
select(Region, Sales) %>%
tail(3)
R Code Explanation
selecttrims columns toRegionandSales.tail(3)returns the last three rows of that data.- The tibble is printed to console.
Expected Output
| Region | Sales |
|---|---|
| South | 1100 |
| East | 1300 |
| East | 1400 |
Workflow
- SAS builds a row‐numbered view and filters by last-three.
- R’s
tail()automatically subsets the final rows.
5. Print a Range of Observations
**Concept**
Choose rows *M* through *N* (e.g., 4–6).**SAS Code**
proc print data=sales_data(firstobs=4 obs=6);
var Region Sales;
run;
SAS Code Explanation
firstobs=4tells SAS to start at the fourth row.obs=6tells it to stop at the sixth.- Together they define a slice of the data set before printing.
**R Code (dplyr + slice)**
sales_data %>%
select(Region, Sales) %>%
slice(4:6)
R Code Explanation
slice(4:6)directly indexes rows 4, 5, and 6.- No prior filtering is needed; R returns exactly that subset.
Expected Output
| Region | Sales |
|---|---|
| South | 1100 |
| East | 1300 |
| East | 1400 |
Workflow
- SAS applies row bounds in the DATA step phase.
- R indexes rows via
slice().
6. Print Arbitrary Observations
**Concept**
Pick any set of row numbers (e.g., 2, 5, 6).**SAS Code**
data want;
set sales_data;
if _n_ in (2,5,6);
run;
proc print data=want;
run;
SAS Code Explanation
- The DATA step reads
sales_datarow by row, with automatic counter_n_. - The
if _n_ in (…)filter keeps only specified rows. wantis written, then PROC PRINT prints that smaller table.
**R Code (dplyr + slice)**
sales_data %>%
select(Region, Sales) %>%
slice(c(2, 5, 6))
R Code Explanation
slice(c(2, 5, 6))tells dplyr exactly which rows to return.- Under the hood, it subsets the data frame by integer positions.
Expected Output
| Region | Sales |
|---|---|
| North | 1500 |
| East | 1300 |
| East | 1400 |
Workflow
- SAS filters via DATA step logic.
- R directly indexes via
slice().
7. Column-wise Totals
**Concept**
Append a grand-total row for numeric columns.**SAS Code**
proc print data=sales_data noobs;
var Sales Profit;
sum Sales Profit;
run;
SAS Code Explanation
noobssuppresses the Obs column.var Sales Profitselects only those two variables.sum Sales Profittells PROC PRINT to accumulate each column’s total as it reads rows.- At the end, PROC PRINT outputs a summary row with column sums.
**R Code (janitor)**
library(dplyr)
library(janitor)
sales_data %>%
select(Sales, Profit) %>%
adorn_totals("row")
R Code Explanation
adorn_totals("row")computescolSums()for numeric columns and appends that as a new row labeled “Total”.- Under the hood,
janitorvets column types, sums, then usesbind_rows().
Input
| Sales | Profit |
|---|---|
| 1200 | 200 |
| 1500 | 250 |
| 900 | 100 |
Expected Output
| Sales | Profit |
|---|---|
| 1200 | 200 |
| 1500 | 250 |
| 900 | 100 |
| 3600 | 550 |
Workflow
- SAS aggregates during the printing pass.
- R post-processes with a separate function call.
8. Grouped Sections (BY-group Pages)
**Concept**
Print each level of a grouping variable in its own block.**SAS Code**
proc sort data=sales_data; by Region; run;
proc print data=sales_data;
by Region;
run;
SAS Code Explanation
proc sortorders the data byRegion.proc print … by Region;instructs PROC PRINT to start a new section (and page) for each distinctRegion.- The BY statement adds a heading with the current group value above each block.
**R Code (dplyr + purrr)**
library(dplyr)
library(purrr)
sales_data %>%
group_by(Region) %>%
group_walk(~ print(.x))
R Code Explanation
group_by(Region)creates a grouped data frame keyed byRegion.group_walk(~ print(.x))iterates over each group, passing the subset (.x) to the anonymous function which prints it.- Each print uses console defaults, so you see one block per region.
Input
| Region | Month | Sales |
|---|---|---|
| East | Jan | 1300 |
| West | Feb | 1600 |
| East | Feb | 1400 |
Expected Output
East
| Month | Sales |
|---|---|
| Jan | 1300 |
| Feb | 1400 |
West
| Month | Sales |
|---|---|
| Feb | 1600 |
Workflow
- SAS sorts then PRINT’s BY mechanism handles page breaks and headings.
- R uses grouping and per-group printing in a loop.
9. Custom Formats and Labels
**Concept**
Display numbers or dates with custom formats and override column names.**SAS Code**
proc print data=sales_data label noobs;
var Sales Date;
format Sales dollar8.2 Date monname.;
label Sales="Net Revenue" Date="Month Name";
run;
SAS Code Explanation
labeloption enables custom column headers instead of variable names.formatassigns a numeric or date format to each variable at print time (e.g.,dollar8.2adds$and two decimals,monname.outputs month names).- PROC PRINT applies these formats only for display; data remains unchanged.
**R Code (dplyr + scales)**
library(dplyr)
library(scales)
sales_data %>%
transmute(
`Net Revenue` = dollar(Sales),
`Month Name` = month.name[as.integer(factor(Month, levels = c("Jan", "Feb")))]
)
R Code Explanation
transmute()both transforms and drops all other columns.dollar()fromscalesformats numeric values to currency strings.month.name[Date]uses R’s built-in vector of month names, indexing by the numericDatevalue.- The result is a tibble of character columns ready for printing.
Input
| Date | Sales |
|---|---|
| 1 | 1200 |
| 2 | 1500 |
Expected Output
| Net Revenue | Month Name |
|---|---|
| $1,200.00 | January |
| $1,500.00 | February |
Workflow
- SAS attaches display-only metadata (formats/labels).
- R builds new formatted columns, so types change to character.
10. Using an ID Variable Instead of Obs#
**Concept**
Replace the default “Obs” column with a meaningful key.**SAS Code**
proc print data=sales_data noobs;
id OrderID;
var Region Sales;
run;
SAS Code Explanation
noobsremoves the automatic Obs column.id OrderID;tells PROC PRINT to printOrderIDin the left‐most column, aligning subsequent columns to the right.- The
idvariable is shown once per row in place of the row number.
**R Code (tibble)**
# Dummy data for execution
library(tibble)
sales_data <- tibble::tibble(
OrderID = c("A1", "B2"),
Region = c("North", "South"),
Sales = c(1200, 900)
)
sales_data %>%
column_to_rownames("OrderID") %>%
print()
R Code Explanation
column_to_rownames("OrderID")transforms theOrderIDcolumn into row names.- When printing a
data.framewith row names, R shows them in the left margin—functionally the same as SAS’s ID.
Input
| OrderID | Region | Sales |
|---|---|---|
| A1 | North | 1200 |
| B2 | South | 900 |
Expected Output
| Region | Sales | |
|---|---|---|
| A1 | North | 1200 |
| B2 | South | 900 |
Workflow
- SAS uses the ID statement in PROC PRINT.
- R manipulates row names before invoking
print().
11. Styled HTML / PDF Tables
**Concept**
Produce publication-ready output with fonts, colors, and footnotes.**SAS Code**
ods html file="report.html" style=Journal;
proc print data=sales_data; run;
ods html close;
SAS Code Explanation
ods html file=... style=Journal;opens an HTML ODS destination with a predefined style.- PROC PRINT writes its output into that HTML file, inheriting the style’s CSS.
ods html close;finalizes and closes the file.
**R Code (knitr + kableExtra)**
library(knitr)
library(kableExtra)
sales_data %>%
kable("html", caption="Monthly Sales") %>%
kable_styling(bootstrap_options=c("striped","hover"))
R Code Explanation
kable("html")converts the tibble into an HTML<table>string.caption="Monthly Sales"adds a<caption>.kable_styling()injects Bootstrap classes (striped,hover) for styling.- The result can be rendered in R Markdown to HTML or PDF.
Input
Same sales_data as above.
Expected Output
An HTML table with striped rows, hover highlights, and a caption.
Workflow
- SAS wraps the PROC PRINT output in ODS markup and CSS.
- R builds HTML and CSS classes via
kableandkable_styling.
12. Interactive Web Tables (R Only)
**Concept**
Create sortable, filterable, paginated tables in a browser.**R Code (DT)**
library(DT)
datatable(
sales_data,
options=list(pageLength=5, autoWidth=TRUE)
)
R Code Explanation
DT::datatable()takes anydata.frameand turns it into an HTML widget.- Under the hood, it generates JavaScript (DataTables.js) plus JSON data.
- Options like
pageLengthandautoWidthconfigure pagination and column sizing. - When viewed in an R Markdown document, Shiny app, or Jupyter notebook, you get interactive controls (search box, column sort, etc.).
Workflow
DT renders your data as an embedded interactive widget, enabling on-the-fly filtering and pagination.
**Resource download links**
1.5.7.-SAS-PROC-PRINT-and-R-Equivalents.zip