Subsets of DataFrames - Maple Help

Subsets of DataFrames

Subsetting is an important component of data manipulation. The DataFrame has a powerful indexable structure that makes it possible to access subsets of data that meet given criteria.

The following example worksheet gives several examples for subsetting data frames.

 > restart;
 > interface(rtablesize = 15):

Removing Variables or Observations by Indexing

A common operation when subsetting data frames is simply to remove certain rows or columns.

To begin, we load the canada_crimes data set. This data set features six variables and 13 rows of observations on aggregated crime statistics per 100,000 people collected in 2014.

 ${\mathrm{DataFrame}}{}\left(\left[\begin{array}{ccccc}1276.15& 3317.03& 1010.67& 348.97& 267.94\\ 824.43& 3294.3& 572.18& 348.64& 215.34\\ 1241.05& 3307.85& 902.76& 368.42& 375.11\\ 1164.32& 2611.17& 712.02& 298.71& 283.45\\ 940.52& 2100.84& 450.29& 511.18& 314.74\\ 786.62& 2292.66& 476.48& 211.57& 258.15\\ 1712.97& 4311.48& 1689.72& 276.28& 362.78\\ 1963.46& 5627.55& 2913.78& 886.34& 692.9\\ 1243.83& 4308.67& 1497.54& 466.12& 371.43\\ 1148.42& 4886.1& 1564.03& 350.85& 682.27\\ 4546.7& 9353.6& 10019.17& 1689.95& 1013.42\\ 6911.49& 23171.26& 13834.45& 1535.89& 1331.87\\ 7934.95& 13778.87& 8902.56& 639.61& 759.87\end{array}\right]{,}{\mathrm{rows}}{=}\left[{\mathrm{Newfoundland and Labrador}}{,}{\mathrm{Prince Edward Island}}{,}{\mathrm{Nova Scotia}}{,}{\mathrm{New Brunswick}}{,}{\mathrm{Quebec}}{,}{\mathrm{Ontario}}{,}{\mathrm{Manitoba}}{,}{\mathrm{Saskatchewan}}{,}{\mathrm{Alberta}}{,}{\mathrm{British Columbia}}{,}{\mathrm{Yukon}}{,}{\mathrm{Northwest Territories}}{,}{\mathrm{Nunavut}}\right]{,}{\mathrm{columns}}{=}\left[{\mathrm{Violent Crime}}{,}{\mathrm{Property Crime}}{,}{\mathrm{Other Criminal Code}}{,}{\mathrm{Criminal Code Traffic}}{,}{\mathrm{Federal Statute}}\right]\right)$ (1)

The variables for this DataFrame are:

 > ColumnLabels( data );
 $\left[{\mathrm{Violent Crime}}{,}{\mathrm{Property Crime}}{,}{\mathrm{Other Criminal Code}}{,}{\mathrm{Criminal Code Traffic}}{,}{\mathrm{Federal Statute}}\right]$ (2)

In order to return subsets of the DataFrame, we can simply index into the DataFrame. For example, to return the subset of data contained in the "Violent Crime" DataSeries:

 > data[ Violent Crime ];
 ${\mathrm{DataSeries}}{}\left(\left[\begin{array}{ccccccccccccc}1276.15& 824.43& 1241.05& 1164.32& 940.52& 786.62& 1712.97& 1963.46& 1243.83& 1148.42& 4546.7& 6911.49& 7934.95\end{array}\right]{,}{\mathrm{labels}}{=}\left[{\mathrm{Newfoundland and Labrador}}{,}{\mathrm{Prince Edward Island}}{,}{\mathrm{Nova Scotia}}{,}{\mathrm{New Brunswick}}{,}{\mathrm{Quebec}}{,}{\mathrm{Ontario}}{,}{\mathrm{Manitoba}}{,}{\mathrm{Saskatchewan}}{,}{\mathrm{Alberta}}{,}{\mathrm{British Columbia}}{,}{\mathrm{Yukon}}{,}{\mathrm{Northwest Territories}}{,}{\mathrm{Nunavut}}\right]{,}{\mathrm{datatype}}{=}{\mathrm{anything}}\right)$ (3)

It is also possible to index the columns of the DataFrame using the integer index values. The following returns the second and third columns:

 > data[ 2..3 ];
 ${\mathrm{DataFrame}}{}\left(\left[\begin{array}{cc}3317.03& 1010.67\\ 3294.3& 572.18\\ 3307.85& 902.76\\ 2611.17& 712.02\\ 2100.84& 450.29\\ 2292.66& 476.48\\ 4311.48& 1689.72\\ 5627.55& 2913.78\\ 4308.67& 1497.54\\ 4886.1& 1564.03\\ 9353.6& 10019.17\\ 23171.26& 13834.45\\ 13778.87& 8902.56\end{array}\right]{,}{\mathrm{rows}}{=}\left[{\mathrm{Newfoundland and Labrador}}{,}{\mathrm{Prince Edward Island}}{,}{\mathrm{Nova Scotia}}{,}{\mathrm{New Brunswick}}{,}{\mathrm{Quebec}}{,}{\mathrm{Ontario}}{,}{\mathrm{Manitoba}}{,}{\mathrm{Saskatchewan}}{,}{\mathrm{Alberta}}{,}{\mathrm{British Columbia}}{,}{\mathrm{Yukon}}{,}{\mathrm{Northwest Territories}}{,}{\mathrm{Nunavut}}\right]{,}{\mathrm{columns}}{=}\left[{\mathrm{Property Crime}}{,}{\mathrm{Other Criminal Code}}\right]\right)$ (4)

The following returns the second, third, and fifth columns:

 > data[ [ 2, 3, 5 ] ];
 ${\mathrm{DataFrame}}{}\left(\left[\begin{array}{ccc}3317.03& 1010.67& 267.94\\ 3294.3& 572.18& 215.34\\ 3307.85& 902.76& 375.11\\ 2611.17& 712.02& 283.45\\ 2100.84& 450.29& 314.74\\ 2292.66& 476.48& 258.15\\ 4311.48& 1689.72& 362.78\\ 5627.55& 2913.78& 692.9\\ 4308.67& 1497.54& 371.43\\ 4886.1& 1564.03& 682.27\\ 9353.6& 10019.17& 1013.42\\ 23171.26& 13834.45& 1331.87\\ 13778.87& 8902.56& 759.87\end{array}\right]{,}{\mathrm{rows}}{=}\left[{\mathrm{Newfoundland and Labrador}}{,}{\mathrm{Prince Edward Island}}{,}{\mathrm{Nova Scotia}}{,}{\mathrm{New Brunswick}}{,}{\mathrm{Quebec}}{,}{\mathrm{Ontario}}{,}{\mathrm{Manitoba}}{,}{\mathrm{Saskatchewan}}{,}{\mathrm{Alberta}}{,}{\mathrm{British Columbia}}{,}{\mathrm{Yukon}}{,}{\mathrm{Northwest Territories}}{,}{\mathrm{Nunavut}}\right]{,}{\mathrm{columns}}{=}\left[{\mathrm{Property Crime}}{,}{\mathrm{Other Criminal Code}}{,}{\mathrm{Federal Statute}}\right]\right)$ (5)

It is also possible to subset the data by indexing the DataFrame by certain rows. The following returns the row of observations corresponding for "Ontario":

 > data[ Ontario, .. ];
 ${\mathrm{DataSeries}}{}\left(\left[\begin{array}{ccccc}786.62& 2292.66& 476.48& 211.57& 258.15\end{array}\right]{,}{\mathrm{labels}}{=}\left[{\mathrm{Violent Crime}}{,}{\mathrm{Property Crime}}{,}{\mathrm{Other Criminal Code}}{,}{\mathrm{Criminal Code Traffic}}{,}{\mathrm{Federal Statute}}\right]{,}{\mathrm{datatype}}{=}{\mathrm{anything}}\right)$ (6)

Similar to indexing by columns, it is also possible to use the row numbers:

 > data[ 2, .. ];
 ${\mathrm{DataSeries}}{}\left(\left[\begin{array}{ccccc}824.43& 3294.3& 572.18& 348.64& 215.34\end{array}\right]{,}{\mathrm{labels}}{=}\left[{\mathrm{Violent Crime}}{,}{\mathrm{Property Crime}}{,}{\mathrm{Other Criminal Code}}{,}{\mathrm{Criminal Code Traffic}}{,}{\mathrm{Federal Statute}}\right]{,}{\mathrm{datatype}}{=}{\mathrm{anything}}\right)$ (7)

Filtering Observations

While the index notation for subsetting data frames is powerful for retrieving observations in known rows or columns, it is often more desirable to return rows corresponding to observations that meet a given criteria. For example, say we wanted to known which Canadian province or territory has a "Criminal Code traffic" rate that is greater than 500 per 100,000.

To begin, we will return the "Criminal Code Traffic" column and simply read off the corresponding rows:

 > data[ Criminal Code Traffic ];
 ${\mathrm{DataSeries}}{}\left(\left[\begin{array}{ccccccccccccc}348.97& 348.64& 368.42& 298.71& 511.18& 211.57& 276.28& 886.34& 466.12& 350.85& 1689.95& 1535.89& 639.61\end{array}\right]{,}{\mathrm{labels}}{=}\left[{\mathrm{Newfoundland and Labrador}}{,}{\mathrm{Prince Edward Island}}{,}{\mathrm{Nova Scotia}}{,}{\mathrm{New Brunswick}}{,}{\mathrm{Quebec}}{,}{\mathrm{Ontario}}{,}{\mathrm{Manitoba}}{,}{\mathrm{Saskatchewan}}{,}{\mathrm{Alberta}}{,}{\mathrm{British Columbia}}{,}{\mathrm{Yukon}}{,}{\mathrm{Northwest Territories}}{,}{\mathrm{Nunavut}}\right]{,}{\mathrm{datatype}}{=}{\mathrm{anything}}\right)$ (8)

Now this approach is fine for smaller data frames, but it is much easier to simply query a DataFrame using an element-wise logical operator to first see which (if any) observations match the criteria:

 > data[ Criminal Code Traffic ] >~ 500;
 ${\mathrm{DataSeries}}{}\left(\left[\begin{array}{ccccccccccccc}\mathrm{false}& \mathrm{false}& \mathrm{false}& \mathrm{false}& \mathrm{true}& \mathrm{false}& \mathrm{false}& \mathrm{true}& \mathrm{false}& \mathrm{false}& \mathrm{true}& \mathrm{true}& \mathrm{true}\end{array}\right]{,}{\mathrm{labels}}{=}\left[{\mathrm{Newfoundland and Labrador}}{,}{\mathrm{Prince Edward Island}}{,}{\mathrm{Nova Scotia}}{,}{\mathrm{New Brunswick}}{,}{\mathrm{Quebec}}{,}{\mathrm{Ontario}}{,}{\mathrm{Manitoba}}{,}{\mathrm{Saskatchewan}}{,}{\mathrm{Alberta}}{,}{\mathrm{British Columbia}}{,}{\mathrm{Yukon}}{,}{\mathrm{Northwest Territories}}{,}{\mathrm{Nunavut}}\right]{,}{\mathrm{datatype}}{=}{\mathrm{truefalseFAIL}}\right)$ (9)

This returns a truth table, whose entries return a true, false, or fail result depending on if the given observation meets the criteria. In addition, if the DataFrame is indexed by a truth table, a filtered subset is returned: In addition, if the DataFrame is indexed by a truth table, a filtered subset is returned:

 > data[ data[ Criminal Code Traffic ] >~ 500 ];
 ${\mathrm{DataFrame}}{}\left(\left[\begin{array}{ccccc}940.52& 2100.84& 450.29& 511.18& 314.74\\ 1963.46& 5627.55& 2913.78& 886.34& 692.9\\ 4546.7& 9353.6& 10019.17& 1689.95& 1013.42\\ 6911.49& 23171.26& 13834.45& 1535.89& 1331.87\\ 7934.95& 13778.87& 8902.56& 639.61& 759.87\end{array}\right]{,}{\mathrm{rows}}{=}\left[{\mathrm{Quebec}}{,}{\mathrm{Saskatchewan}}{,}{\mathrm{Yukon}}{,}{\mathrm{Northwest Territories}}{,}{\mathrm{Nunavut}}\right]{,}{\mathrm{columns}}{=}\left[{\mathrm{Violent Crime}}{,}{\mathrm{Property Crime}}{,}{\mathrm{Other Criminal Code}}{,}{\mathrm{Criminal Code Traffic}}{,}{\mathrm{Federal Statute}}\right]\right)$ (10)

The with command is useful for simplifying the syntax for querying DataFrames. with creates named variables corresponding to each of the column labels in a given DataFrame.

 > with( data );
 $\left[{\mathrm{Violent Crime}}{,}{\mathrm{Property Crime}}{,}{\mathrm{Other Criminal Code}}{,}{\mathrm{Criminal Code Traffic}}{,}{\mathrm{Federal Statute}}\right]$ (11)

Each column of the DataFrame can be called using its variable name:

 > Criminal Code Traffic;
 ${\mathrm{DataSeries}}{}\left(\left[\begin{array}{ccccccccccccc}348.97& 348.64& 368.42& 298.71& 511.18& 211.57& 276.28& 886.34& 466.12& 350.85& 1689.95& 1535.89& 639.61\end{array}\right]{,}{\mathrm{labels}}{=}\left[{\mathrm{Newfoundland and Labrador}}{,}{\mathrm{Prince Edward Island}}{,}{\mathrm{Nova Scotia}}{,}{\mathrm{New Brunswick}}{,}{\mathrm{Quebec}}{,}{\mathrm{Ontario}}{,}{\mathrm{Manitoba}}{,}{\mathrm{Saskatchewan}}{,}{\mathrm{Alberta}}{,}{\mathrm{British Columbia}}{,}{\mathrm{Yukon}}{,}{\mathrm{Northwest Territories}}{,}{\mathrm{Nunavut}}\right]{,}{\mathrm{datatype}}{=}{\mathrm{anything}}\right)$ (12)

With bound labels, the following returns the rows where the "Federal Statute" rate is less than or equal to 300 per 100,000:

 > Federal Statute <=~ 300;
 ${\mathrm{DataSeries}}{}\left(\left[\begin{array}{ccccccccccccc}\mathrm{true}& \mathrm{true}& \mathrm{false}& \mathrm{true}& \mathrm{false}& \mathrm{true}& \mathrm{false}& \mathrm{false}& \mathrm{false}& \mathrm{false}& \mathrm{false}& \mathrm{false}& \mathrm{false}\end{array}\right]{,}{\mathrm{labels}}{=}\left[{\mathrm{Newfoundland and Labrador}}{,}{\mathrm{Prince Edward Island}}{,}{\mathrm{Nova Scotia}}{,}{\mathrm{New Brunswick}}{,}{\mathrm{Quebec}}{,}{\mathrm{Ontario}}{,}{\mathrm{Manitoba}}{,}{\mathrm{Saskatchewan}}{,}{\mathrm{Alberta}}{,}{\mathrm{British Columbia}}{,}{\mathrm{Yukon}}{,}{\mathrm{Northwest Territories}}{,}{\mathrm{Nunavut}}\right]{,}{\mathrm{datatype}}{=}{\mathrm{truefalseFAIL}}\right)$ (13)
 > data[ Federal Statute <=~ 300 ];
 ${\mathrm{DataFrame}}{}\left(\left[\begin{array}{ccccc}1276.15& 3317.03& 1010.67& 348.97& 267.94\\ 824.43& 3294.3& 572.18& 348.64& 215.34\\ 1164.32& 2611.17& 712.02& 298.71& 283.45\\ 786.62& 2292.66& 476.48& 211.57& 258.15\end{array}\right]{,}{\mathrm{rows}}{=}\left[{\mathrm{Newfoundland and Labrador}}{,}{\mathrm{Prince Edward Island}}{,}{\mathrm{New Brunswick}}{,}{\mathrm{Ontario}}\right]{,}{\mathrm{columns}}{=}\left[{\mathrm{Violent Crime}}{,}{\mathrm{Property Crime}}{,}{\mathrm{Other Criminal Code}}{,}{\mathrm{Criminal Code Traffic}}{,}{\mathrm{Federal Statute}}\right]\right)$ (14)

It is also possible to filter the DataFrame using multiple queries. When combining queries, the logical operators and and or are used to find either the intersection or union of truth tables, respectively. For example, the following returns the province or territory with "Violent Crime" less than 1000 and "Property Crime" greater than 3000.

 > Violent Crime <~ 1000 and Property Crime >~ 3000;
 ${\mathrm{DataSeries}}{}\left(\left[\begin{array}{ccccccccccccc}\mathrm{false}& \mathrm{true}& \mathrm{false}& \mathrm{false}& \mathrm{false}& \mathrm{false}& \mathrm{false}& \mathrm{false}& \mathrm{false}& \mathrm{false}& \mathrm{false}& \mathrm{false}& \mathrm{false}\end{array}\right]{,}{\mathrm{labels}}{=}\left[{\mathrm{Newfoundland and Labrador}}{,}{\mathrm{Prince Edward Island}}{,}{\mathrm{Nova Scotia}}{,}{\mathrm{New Brunswick}}{,}{\mathrm{Quebec}}{,}{\mathrm{Ontario}}{,}{\mathrm{Manitoba}}{,}{\mathrm{Saskatchewan}}{,}{\mathrm{Alberta}}{,}{\mathrm{British Columbia}}{,}{\mathrm{Yukon}}{,}{\mathrm{Northwest Territories}}{,}{\mathrm{Nunavut}}\right]{,}{\mathrm{datatype}}{=}{\mathrm{truefalseFAIL}}\right)$ (15)

From the truth table, only Prince Edward Island matches this criteria.

 > data [ Violent Crime <~ 1000 and Property Crime >~ 3000 ];
 ${\mathrm{DataFrame}}{}\left(\left[\begin{array}{ccccc}824.43& 3294.3& 572.18& 348.64& 215.34\end{array}\right]{,}{\mathrm{rows}}{=}\left[{\mathrm{Prince Edward Island}}\right]{,}{\mathrm{columns}}{=}\left[{\mathrm{Violent Crime}}{,}{\mathrm{Property Crime}}{,}{\mathrm{Other Criminal Code}}{,}{\mathrm{Criminal Code Traffic}}{,}{\mathrm{Federal Statute}}\right]\right)$ (16)

It can be useful to find the union of queries by using the or logical operator. For example, the following returns observations for which the "Other Criminal Code" rate is greater than 2500 per 100,000 or the observations for which the "Criminal Code Traffic" rate is greater than 500 per 100,000:

 > data [ Other Criminal Code >~ 2500 or Criminal Code Traffic >~ 500 ];
 ${\mathrm{DataFrame}}{}\left(\left[\begin{array}{ccccc}940.52& 2100.84& 450.29& 511.18& 314.74\\ 1963.46& 5627.55& 2913.78& 886.34& 692.9\\ 4546.7& 9353.6& 10019.17& 1689.95& 1013.42\\ 6911.49& 23171.26& 13834.45& 1535.89& 1331.87\\ 7934.95& 13778.87& 8902.56& 639.61& 759.87\end{array}\right]{,}{\mathrm{rows}}{=}\left[{\mathrm{Quebec}}{,}{\mathrm{Saskatchewan}}{,}{\mathrm{Yukon}}{,}{\mathrm{Northwest Territories}}{,}{\mathrm{Nunavut}}\right]{,}{\mathrm{columns}}{=}\left[{\mathrm{Violent Crime}}{,}{\mathrm{Property Crime}}{,}{\mathrm{Other Criminal Code}}{,}{\mathrm{Criminal Code Traffic}}{,}{\mathrm{Federal Statute}}\right]\right)$ (17)
 >