Comparison with SAS
For potential users coming from SAS this page is meant to demonstrate how different SAS operations would be performed in pandas.
If you’re new to pandas, you might want to first read through 10 Minutes to pandas to familiarize yourself with the library.
As is customary, we import pandas and NumPy as follows:
In [1]: import pandas as pd In [2]: import numpy as np
Note
Throughout this tutorial, the pandas DataFrame
will be displayed by calling df.head()
, which displays the first N (default 5) rows of the DataFrame
. This is often used in interactive work (e.g. Jupyter notebook or terminal) - the equivalent in SAS would be:
proc print data=df(obs=5); run;
Data Structures
General Terminology Translation
pandas | SAS |
---|---|
DataFrame | data set |
column | variable |
row | observation |
groupby | BY-group |
NaN | . |
DataFrame
/ Series
A DataFrame
in pandas is analogous to a SAS data set - a two-dimensional data source with labeled columns that can be of different types. As will be shown in this document, almost any operation that can be applied to a data set using SAS’s DATA
step, can also be accomplished in pandas.
A Series
is the data structure that represents one column of a DataFrame
. SAS doesn’t have a separate data structure for a single column, but in general, working with a Series
is analogous to referencing a column in the DATA
step.
Index
Every DataFrame
and Series
has an Index
- which are labels on the rows of the data. SAS does not have an exactly analogous concept. A data set’s rows are essentially unlabeled, other than an implicit integer index that can be accessed during the DATA
step (_N_
).
In pandas, if no index is specified, an integer index is also used by default (first row = 0, second row = 1, and so on). While using a labeled Index
or MultiIndex
can enable sophisticated analyses and is ultimately an important part of pandas to understand, for this comparison we will essentially ignore the Index
and just treat the DataFrame
as a collection of columns. Please see the indexing documentation for much more on how to use an Index
effectively.
Data Input / Output
Constructing a DataFrame from Values
A SAS data set can be built from specified values by placing the data after a datalines
statement and specifying the column names.
data df; input x y; datalines; 1 2 3 4 5 6 ; run;
A pandas DataFrame
can be constructed in many different ways, but for a small number of values, it is often convenient to specify it as a Python dictionary, where the keys are the column names and the values are the data.
In [3]: df = pd.DataFrame({ ...: 'x': [1, 3, 5], ...: 'y': [2, 4, 6]}) ...: In [4]: df Out[4]: x y 0 1 2 1 3 4 2 5 6
Reading External Data
Like SAS, pandas provides utilities for reading in data from many formats. The tips
dataset, found within the pandas tests (csv) will be used in many of the following examples.
SAS provides PROC IMPORT
to read csv data into a data set.
proc import datafile='tips.csv' dbms=csv out=tips replace; getnames=yes; run;
The pandas method is read_csv()
, which works similarly.
In [5]: url = 'https://raw.github.com/pandas-dev/pandas/master/pandas/tests/data/tips.csv' In [6]: tips = pd.read_csv(url) In [7]: tips.head() Out[7]: total_bill tip sex smoker day time size 0 16.99 1.01 Female No Sun Dinner 2 1 10.34 1.66 Male No Sun Dinner 3 2 21.01 3.50 Male No Sun Dinner 3 3 23.68 3.31 Male No Sun Dinner 2 4 24.59 3.61 Female No Sun Dinner 4
Like PROC IMPORT
, read_csv
can take a number of parameters to specify how the data should be parsed. For example, if the data was instead tab delimited, and did not have column names, the pandas command would be:
tips = pd.read_csv('tips.csv', sep='\t', header=None) # alternatively, read_table is an alias to read_csv with tab delimiter tips = pd.read_table('tips.csv', header=None)
In addition to text/csv, pandas supports a variety of other data formats such as Excel, HDF5, and SQL databases. These are all read via a pd.read_*
function. See the IO documentation for more details.
Exporting Data
The inverse of PROC IMPORT
in SAS is PROC EXPORT
proc export data=tips outfile='tips2.csv' dbms=csv; run;
Similarly in pandas, the opposite of read_csv
is to_csv()
, and other data formats follow a similar api.
tips.to_csv('tips2.csv')
Data Operations
Operations on Columns
In the DATA
step, arbitrary math expressions can be used on new or existing columns.
data tips; set tips; total_bill = total_bill - 2; new_bill = total_bill / 2; run;
pandas provides similar vectorized operations by specifying the individual Series
in the DataFrame
. New columns can be assigned in the same way.
In [8]: tips['total_bill'] = tips['total_bill'] - 2 In [9]: tips['new_bill'] = tips['total_bill'] / 2.0 In [10]: tips.head() Out[10]: total_bill tip sex smoker day time size new_bill 0 14.99 1.01 Female No Sun Dinner 2 7.495 1 8.34 1.66 Male No Sun Dinner 3 4.170 2 19.01 3.50 Male No Sun Dinner 3 9.505 3 21.68 3.31 Male No Sun Dinner 2 10.840 4 22.59 3.61 Female No Sun Dinner 4 11.295
Filtering
Filtering in SAS is done with an if
or where
statement, on one or more columns.
data tips; set tips; if total_bill > 10; run; data tips; set tips; where total_bill > 10; /* equivalent in this case - where happens before the DATA step begins and can also be used in PROC statements */ run;
DataFrames can be filtered in multiple ways; the most intuitive of which is using boolean indexing
In [11]: tips[tips['total_bill'] > 10].head() Out[11]: total_bill tip sex smoker day time size 0 14.99 1.01 Female No Sun Dinner 2 2 19.01 3.50 Male No Sun Dinner 3 3 21.68 3.31 Male No Sun Dinner 2 4 22.59 3.61 Female No Sun Dinner 4 5 23.29 4.71 Male No Sun Dinner 4
If/Then Logic
In SAS, if/then logic can be used to create new columns.
data tips; set tips; format bucket $4.; if total_bill < 10 then bucket = 'low'; else bucket = 'high'; run;
The same operation in pandas can be accomplished using the where
method from numpy
.
In [12]: tips['bucket'] = np.where(tips['total_bill'] < 10, 'low', 'high') In [13]: tips.head() Out[13]: total_bill tip sex smoker day time size bucket 0 14.99 1.01 Female No Sun Dinner 2 high 1 8.34 1.66 Male No Sun Dinner 3 low 2 19.01 3.50 Male No Sun Dinner 3 high 3 21.68 3.31 Male No Sun Dinner 2 high 4 22.59 3.61 Female No Sun Dinner 4 high
Date Functionality
SAS provides a variety of functions to do operations on date/datetime columns.
data tips; set tips; format date1 date2 date1_plusmonth mmddyy10.; date1 = mdy(1, 15, 2013); date2 = mdy(2, 15, 2015); date1_year = year(date1); date2_month = month(date2); * shift date to beginning of next interval; date1_next = intnx('MONTH', date1, 1); * count intervals between dates; months_between = intck('MONTH', date1, date2); run;
The equivalent pandas operations are shown below. In addition to these functions pandas supports other Time Series features not available in Base SAS (such as resampling and custom offsets) - see the timeseries documentation for more details.
In [14]: tips['date1'] = pd.Timestamp('2013-01-15') In [15]: tips['date2'] = pd.Timestamp('2015-02-15') In [16]: tips['date1_year'] = tips['date1'].dt.year In [17]: tips['date2_month'] = tips['date2'].dt.month In [18]: tips['date1_next'] = tips['date1'] + pd.offsets.MonthBegin() In [19]: tips['months_between'] = (tips['date2'].dt.to_period('M') - ....: tips['date1'].dt.to_period('M')) ....: In [20]: tips[['date1','date2','date1_year','date2_month', ....: 'date1_next','months_between']].head() ....: Out[20]: date1 date2 date1_year date2_month date1_next months_between 0 2013-01-15 2015-02-15 2013 2 2013-02-01 25 1 2013-01-15 2015-02-15 2013 2 2013-02-01 25 2 2013-01-15 2015-02-15 2013 2 2013-02-01 25 3 2013-01-15 2015-02-15 2013 2 2013-02-01 25 4 2013-01-15 2015-02-15 2013 2 2013-02-01 25
Selection of Columns
SAS provides keywords in the DATA
step to select, drop, and rename columns.
data tips; set tips; keep sex total_bill tip; run; data tips; set tips; drop sex; run; data tips; set tips; rename total_bill=total_bill_2; run;
The same operations are expressed in pandas below.
# keep In [21]: tips[['sex', 'total_bill', 'tip']].head() Out[21]: sex total_bill tip 0 Female 14.99 1.01 1 Male 8.34 1.66 2 Male 19.01 3.50 3 Male 21.68 3.31 4 Female 22.59 3.61 # drop In [22]: tips.drop('sex', axis=1).head()
© 2008–2012, AQR Capital Management, LLC, Lambda Foundry, Inc. and PyData Development Team
Licensed under the 3-clause BSD License.
https://pandas.pydata.org/pandas-docs/version/0.23.4/comparison_with_sas.html