4.2 Data frames, Series and arrays
We have now come across three important structures that Python uses to store and access data:
- arrays
- data frames
- series
Here we stop to go back over the differences between these structures, and how to convert between them.
Data frames
We start by loading a data frame from a Comma Separated Value file (CSV file).
The data file we will load is a table with average https://ratemyprofessors.com scores across all professors teaching a particular academic discipline.
See the array indexing page for more detail.
Each row in this table corresponds to one discipline. Each column corresponds to a different rating.
If you are running on your laptop, you should download the rate_my_course.csv file to the same directory as this notebook.
# Load the Numpy library, rename to "np"
import numpy as np
# Load the Pandas data science library, rename to "pd"
import pandas as pd
# Read the file.
courses = pd.read_csv('rate_my_course.csv')
# Show the first five rows.
courses.head()
Discipline | Number of Professors | Clarity | Helpfulness | Overall Quality | Easiness | |
---|---|---|---|---|---|---|
0 | English | 23343 | 3.756147 | 3.821866 | 3.791364 | 3.162754 |
1 | Mathematics | 22394 | 3.487379 | 3.641526 | 3.566867 | 3.063322 |
2 | Biology | 11774 | 3.608331 | 3.701530 | 3.657641 | 2.710459 |
3 | Psychology | 11179 | 3.909520 | 3.887536 | 3.900949 | 3.316210 |
4 | History | 11145 | 3.788818 | 3.753642 | 3.773746 | 3.053803 |
The pd.read_csv
function returned this table in a structure called a data frame.
type(courses)
pandas.core.frame.DataFrame
The data frame is a two-dimensional structure. It has rows, and columns. We can see the number of rows and columns with:
courses.shape
(75, 6)
This means there are 75 rows. In this case, each row corresponds to one discpline.
There are 6 columns. In this case, each column corresponds to a different student rating.
Passing the data frame to the Python len
function shows us the number of rows:
len(courses)
75
Indexing into data frames
There are two simple ways of indexing into data frames.
We index into a data frame to get a subset of of the data.
To index into anything, we can give the name of thing - in this case courses
- followed by an opening square bracket [
, followed by something to specify which subset of the data we want, followed by a closing square bracket ]
.
The two simple ways of indexing into a data frame are:
- Indexing with a string to get a column.
- Indexing with a Boolean sequence to get a subset of the rows.
When we index with a string, the string should be a column name:
easiness = courses['Easiness']
The result is a series:
type(easiness)
pandas.core.series.Series
The Series is a structure that holds the data for a single column.
easiness
0 3.162754
1 3.063322
2 2.710459
3 3.316210
4 3.053803
5 2.652054
6 3.379829
7 3.172033
8 3.057758
9 2.910078
10 3.115357
11 3.395819
12 3.132724
13 2.784706
14 3.277406
15 2.854413
16 2.785668
17 3.248045
18 3.430916
19 3.542273
20 3.138076
21 3.468012
22 3.344138
23 2.885714
24 3.469440
25 3.244433
26 3.194300
27 3.338846
28 3.144567
29 2.868762
...
45 3.324156
46 3.276412
47 3.180846
48 3.423021
49 3.674701
50 3.314322
51 3.199716
52 2.978182
53 2.977254
54 3.471498
55 2.825019
56 3.178866
57 2.887940
58 3.323158
59 3.365544
60 2.830455
61 3.606082
62 3.002857
63 3.267099
64 3.882635
65 3.275238
66 3.402397
67 3.541439
68 3.468333
69 2.969417
70 2.863504
71 3.106727
72 3.309636
73 2.799135
74 3.109118
Name: Easiness, Length: 75, dtype: float64
We will come back to the Series soon.
Notice that, if your string specifying the column name does not match a column name exactly, you will get a long error. This gives you some practice in reading long error messages - skip to the end first, you will often see the most helpful information there.
# The exact column name starts with capital E
courses['easiness']
KeyError Traceback (most recent call last)
...
KeyError: 'easiness'
You have just seen indexing into the data frame with a string to get the data for one column.
The other simple way of indexing into a data frame is with a Boolean sequence.
A Boolean sequence is a sequence of values, all of which are either True or False. Examples of sequences are series and arrays.
For example, imagine we only wanted to look at courses with an easiness rating of greater than 3.25.
We first make the Boolean sequence, by asking the question > 3.25
of the values in the “Easiness” column, like this:
is_easy = easiness > 3.25
This is a series that has True and False values:
type(is_easy)
pandas.core.series.Series
is_easy
0 False
1 False
2 False
3 True
4 False
5 False
6 True
7 False
8 False
9 False
10 False
11 True
12 False
13 False
14 True
15 False
16 False
17 False
18 True
19 True
20 False
21 True
22 True
23 False
24 True
25 False
26 False
27 True
28 False
29 False
...
45 True
46 True
47 False
48 True
49 True
50 True
51 False
52 False
53 False
54 True
55 False
56 False
57 False
58 True
59 True
60 False
61 True
62 False
63 True
64 True
65 True
66 True
67 True
68 True
69 False
70 False
71 False
72 True
73 False
74 False
Name: Easiness, Length: 75, dtype: bool
It has True values where the corresponding row had an “Easiness” score greater than 3.25, and False values where the corresponding row had an “Easiness” score of less than or equal to 3.25.
We can index into the data frame with this Boolean series.
When we do this, we ask the data frame to give us a new version of itself, that only has the rows where there was a True value in the Boolean series:
easy_courses = courses[is_easy]
The result is a data frame:
type(easy_courses)
pandas.core.frame.DataFrame
The data frame contains only the rows where the “Easiness” score is greater than 3.25:
easy_courses
Discipline | Number of Professors | Clarity | Helpfulness | Overall Quality | Easiness | |
---|---|---|---|---|---|---|
3 | Psychology | 11179 | 3.909520 | 3.887536 | 3.900949 | 3.316210 |
6 | Communications | 6940 | 3.867349 | 3.878602 | 3.875019 | 3.379829 |
11 | Sociology | 4839 | 3.740980 | 3.748169 | 3.746962 | 3.395819 |
14 | Languages | 3867 | 3.772780 | 3.917949 | 3.846951 | 3.277406 |
18 | Education | 2544 | 3.707429 | 3.806128 | 3.758211 | 3.430916 |
19 | Music | 2455 | 3.844509 | 3.787804 | 3.818114 | 3.542273 |
21 | Health | 1937 | 3.891177 | 3.884729 | 3.891213 | 3.468012 |
22 | Humanities | 1897 | 3.806969 | 3.816299 | 3.813569 | 3.344138 |
24 | Criminal Justice | 1786 | 4.056685 | 4.033779 | 4.046702 | 3.469440 |
27 | Social Science | 1412 | 3.683555 | 3.691133 | 3.690262 | 3.338846 |
31 | Theater | 1078 | 3.876633 | 3.821503 | 3.851837 | 3.584508 |
36 | Physical Education | 991 | 4.078698 | 4.030797 | 4.057719 | 3.832250 |
42 | Spanish | 584 | 3.854932 | 3.993716 | 3.928750 | 3.349743 |
43 | Art | 574 | 3.797073 | 3.799216 | 3.800157 | 3.268293 |
44 | Ethnic Studies | 481 | 3.533597 | 3.578337 | 3.556923 | 3.387630 |
45 | Film | 450 | 3.788933 | 3.764689 | 3.778689 | 3.324156 |
46 | Women's Studies | 393 | 3.639949 | 3.652087 | 3.646743 | 3.276412 |
48 | Government | 374 | 3.957540 | 3.916070 | 3.938663 | 3.423021 |
49 | Speech | 351 | 4.133191 | 4.101197 | 4.119345 | 3.674701 |
50 | Social Work | 317 | 3.770726 | 3.834700 | 3.806246 | 3.314322 |
54 | Hospitality | 267 | 3.697228 | 3.744607 | 3.719476 | 3.471498 |
58 | Foreign Language | 209 | 3.914641 | 4.057751 | 3.991675 | 3.323158 |
59 | Religious Studies | 193 | 3.932746 | 3.985751 | 3.962280 | 3.365544 |
61 | Child Development | 171 | 3.950585 | 4.008070 | 3.979766 | 3.606082 |
63 | French | 162 | 3.775370 | 3.943086 | 3.860679 | 3.267099 |
64 | Reading | 148 | 4.159392 | 4.188919 | 4.177500 | 3.882635 |
65 | Modern Languages | 147 | 3.858503 | 3.971293 | 3.916259 | 3.275238 |
66 | Agriculture | 146 | 3.767466 | 3.863219 | 3.820068 | 3.402397 |
67 | Kinesiology | 132 | 3.995000 | 3.972879 | 3.988712 | 3.541439 |
68 | Nutrition | 120 | 3.815167 | 3.844333 | 3.831500 | 3.468333 |
72 | Linguistics | 110 | 3.749000 | 3.834545 | 3.798182 | 3.309636 |
The way this works can be easier to see when we use a smaller data frame.
Here we take the first eight rows from the data frame, by using the head
method.
The head
method can take an argument, which is the number of rows we want.
first_8 = courses.head(8)
The result is a new data frame:
type(first_8)
pandas.core.frame.DataFrame
first_8
Discipline | Number of Professors | Clarity | Helpfulness | Overall Quality | Easiness | |
---|---|---|---|---|---|---|
0 | English | 23343 | 3.756147 | 3.821866 | 3.791364 | 3.162754 |
1 | Mathematics | 22394 | 3.487379 | 3.641526 | 3.566867 | 3.063322 |
2 | Biology | 11774 | 3.608331 | 3.701530 | 3.657641 | 2.710459 |
3 | Psychology | 11179 | 3.909520 | 3.887536 | 3.900949 | 3.316210 |
4 | History | 11145 | 3.788818 | 3.753642 | 3.773746 | 3.053803 |
5 | Chemistry | 7346 | 3.387174 | 3.538980 | 3.465485 | 2.652054 |
6 | Communications | 6940 | 3.867349 | 3.878602 | 3.875019 | 3.379829 |
7 | Business | 6120 | 3.640327 | 3.680503 | 3.663332 | 3.172033 |
We index into the new data frame with a string, to get the “Easiness” column:
easiness_first_8 = first_8["Easiness"]
easiness_first_8
0 3.162754
1 3.063322
2 2.710459
3 3.316210
4 3.053803
5 2.652054
6 3.379829
7 3.172033
Name: Easiness, dtype: float64
This Boolean series has True where the “Easiness” score is greater than 3.25, and False otherwise:
is_easy_first_8 = easiness_first_8 > 3.25
is_easy_first_8
0 False
1 False
2 False
3 True
4 False
5 False
6 True
7 False
Name: Easiness, dtype: bool
We index into the first_8
data frame with this Boolean series, to select the rows where is_easy_first_8
has True, and throw away the rows where it has False.
easy_first_8 = first_8[is_easy_first_8]
easy_first_8
Discipline | Number of Professors | Clarity | Helpfulness | Overall Quality | Easiness | |
---|---|---|---|---|---|---|
3 | Psychology | 11179 | 3.909520 | 3.887536 | 3.900949 | 3.316210 |
6 | Communications | 6940 | 3.867349 | 3.878602 | 3.875019 | 3.379829 |
Oh dear, Psychology looks pretty easy.
Series and array
The series, as you have seen, is the structure that Pandas uses to store the data from a column:
first_8
Discipline | Number of Professors | Clarity | Helpfulness | Overall Quality | Easiness | |
---|---|---|---|---|---|---|
0 | English | 23343 | 3.756147 | 3.821866 | 3.791364 | 3.162754 |
1 | Mathematics | 22394 | 3.487379 | 3.641526 | 3.566867 | 3.063322 |
2 | Biology | 11774 | 3.608331 | 3.701530 | 3.657641 | 2.710459 |
3 | Psychology | 11179 | 3.909520 | 3.887536 | 3.900949 | 3.316210 |
4 | History | 11145 | 3.788818 | 3.753642 | 3.773746 | 3.053803 |
5 | Chemistry | 7346 | 3.387174 | 3.538980 | 3.465485 | 2.652054 |
6 | Communications | 6940 | 3.867349 | 3.878602 | 3.875019 | 3.379829 |
7 | Business | 6120 | 3.640327 | 3.680503 | 3.663332 | 3.172033 |
easiness_first_8 = first_8["Easiness"]
easiness_first_8
0 3.162754
1 3.063322
2 2.710459
3 3.316210
4 3.053803
5 2.652054
6 3.379829
7 3.172033
Name: Easiness, dtype: float64
You can index into a series, but this indexing is powerful and sophisticated, so we will not use that for now.
For now, you can convert the series to an array, like this:
easi_8 = np.array(easiness_first_8)
easi_8
array([3.16275414, 3.06332232, 2.71045949, 3.31620986, 3.0538026 ,
2.65205418, 3.37982853, 3.17203268])
Then you can use the usual array indexing to get the values you want:
# The first value
easi_8[0]
3.1627541447114904
# The first five values
easi_8[:5]
array([3.16275414, 3.06332232, 2.71045949, 3.31620986, 3.0538026 ])
You can think of a data frame as sequence of columns, where each column is series.
Here I take two columns from the data frame, as series:
disciplines = first_8['Discipline']
disciplines
0 English
1 Mathematics
2 Biology
3 Psychology
4 History
5 Chemistry
6 Communications
7 Business
Name: Discipline, dtype: object
clarity = first_8['Clarity']
clarity
0 3.756147
1 3.487379
2 3.608331
3 3.909520
4 3.788818
5 3.387174
6 3.867349
7 3.640327
Name: Clarity, dtype: float64
I can make a new data frame by inserting these two columns:
# A new data frame
thinner_courses = pd.DataFrame()
thinner_courses['Discipline'] = disciplines
thinner_courses['Clarity'] = clarity
thinner_courses
Discipline | Clarity | |
---|---|---|
0 | English | 3.756147 |
1 | Mathematics | 3.487379 |
2 | Biology | 3.608331 |
3 | Psychology | 3.909520 |
4 | History | 3.788818 |
5 | Chemistry | 3.387174 |
6 | Communications | 3.867349 |
7 | Business | 3.640327 |