Unverified Commit dcc91dfb authored by Simon Bowly's avatar Simon Bowly
Browse files

Clean up pandas merging notebooks.

parent 9d5b0c55
This source diff could not be displayed because it is too large. You can view the blob instead.
%% Cell type:code id: tags:
``` python
import pandas as pd
exams = pd.read_csv('ExamMark.csv')
ca = pd.read_csv('CAMark.csv')
```
%% Cell type:code id: tags:
``` python
print(ca)
newrow = pd.DataFrame({'Student ID':[1537],'CA Mark':[55]})
ca = ca.append(newrow,ignore_index=True)
print(ca)
newcolumn = pd.DataFrame([1,1,2,1,3,2],columns=['Course Code'])
print(newcolumn)
nca = pd.concat([ca,newcolumn],axis=1)
print(nca)
coursedict = pd.DataFrame({'Course Code':[1,2,3],
'Degree':['Bachelor of Science','Bachelor of Engineering','Bachelor of IT']})
print(coursedict)
```
%%%% Output: stream
Student ID CA Mark
0 6789 62
1 7410 8
2 7634 34
3 9016 69
4 9532 5
Student ID CA Mark
0 6789 62
1 7410 8
2 7634 34
3 9016 69
4 9532 5
5 1537 55
Course Code
0 1
1 1
2 2
3 1
4 3
5 2
Student ID CA Mark Course Code
0 6789 62 1
1 7410 8 1
2 7634 34 2
3 9016 69 1
4 9532 5 3
5 1537 55 2
Course Code Degree
0 1 Bachelor of Science
1 2 Bachelor of Engineering
2 3 Bachelor of IT
%% Cell type:code id: tags:
``` python
# merged = pd.merge(exams,ca)
merged = pd.merge(nca,coursedict,on='Course Code')
merged
```
%%%% Output: execute_result
Student ID CA Mark Course Code Degree
0 6789 62 1 Bachelor of Science
1 7410 8 1 Bachelor of Science
2 9016 69 1 Bachelor of Science
3 7634 34 2 Bachelor of Engineering
4 1537 55 2 Bachelor of Engineering
5 9532 5 3 Bachelor of IT
%% Cell type:code id: tags:
``` python
ca.columns = ['ID','CA Mark']
```
%% Cell type:code id: tags:
``` python
ca
```
%%%% Output: execute_result
ID CA Mark
0 6789 62
1 7410 8
2 7634 34
3 9016 69
4 9532 5
%% Cell type:code id: tags:
``` python
merged = pd.merge(exams,ca,left_on='Student ID',right_on='ID').drop('ID',axis=1)
```
%% Cell type:code id: tags:
``` python
merged
```
%%%% Output: execute_result
Student ID Firstname Lastname Exam Mark CA Mark
0 7634 James Brown 52 34
1 6789 Ella Fitzgerald 73 62
2 7410 Herbie Hancock 9 8
3 9016 Dolly Parton 87 69
4 9532 Keith Richards 81 5
%% Cell type:code id: tags:
``` python
newrow = pd.DataFrame({'ID':[1537],'CA Mark':[55]})
ca = ca.append(newrow)
ca['ID'] = ca['ID'].astype(str)
exams['Student ID'] = exams['Student ID'].astype(str)
ca1 = ca.set_index('ID').drop('9532')
exams1 = exams.set_index('Student ID')
print(ca1)
print(exam1)
```
%%%% Output: stream
CA Mark
ID
6789 62
7410 8
7634 34
9016 69
1537 55
1537 55
Firstname Lastname Exam Mark
Student ID
7634 James Brown 52
6789 Ella Fitzgerald 73
7410 Herbie Hancock 9
9016 Dolly Parton 87
9532 Keith Richards 81
%% Cell type:code id: tags:
``` python
merged = pd.merge(exams1,ca1,how='left',left_index=True,right_index=True)
merged
```
%%%% Output: execute_result
Firstname Lastname Exam Mark CA Mark
6789 Ella Fitzgerald 73 62.0
7410 Herbie Hancock 9 8.0
7634 James Brown 52 34.0
9016 Dolly Parton 87 69.0
9532 Keith Richards 81 NaN
%% Cell type:code id: tags:
``` python
merged = pd.merge(exams1,ca1,how='right',left_index=True,right_index=True)
merged
```
%%%% Output: execute_result
Firstname Lastname Exam Mark CA Mark
1537 NaN NaN NaN 55
1537 NaN NaN NaN 55
6789 Ella Fitzgerald 73.0 62
7410 Herbie Hancock 9.0 8
7634 James Brown 52.0 34
9016 Dolly Parton 87.0 69
%% Cell type:code id: tags:
``` python
merged = pd.merge(exams1,ca1,how='inner',left_index=True,right_index=True)
merged
```
%%%% Output: execute_result
Firstname Lastname Exam Mark CA Mark
6789 Ella Fitzgerald 73 62
7410 Herbie Hancock 9 8
7634 James Brown 52 34
9016 Dolly Parton 87 69
%% Cell type:code id: tags:
``` python
merged = pd.merge(exams1,ca1,how='outer',left_index=True,right_index=True)
merged
```
%%%% Output: execute_result
Firstname Lastname Exam Mark CA Mark
1537 NaN NaN NaN 55.0
1537 NaN NaN NaN 55.0
6789 Ella Fitzgerald 73.0 62.0
7410 Herbie Hancock 9.0 8.0
7634 James Brown 52.0 34.0
9016 Dolly Parton 87.0 69.0
9532 Keith Richards 81.0 NaN
%% Cell type:code id: tags:
``` python
Generation = pd.read_csv('EnergyGeneration.csv')
Usage = pd.read_csv('EnergyUsage.csv')
Generation['Time'] = pd.to_datetime(Generation['Time'], format="%I:%M:%S %p")
Usage['Time'] = pd.to_datetime(Usage['Time'], format="%I:%M:%S %p")
print(Usage.head())
print(Generation.head())
```
%%%% Output: stream
Time Energy Usage (kW)
0 1900-01-01 00:00:00 0.001851
1 1900-01-01 01:00:00 0.018957
2 1900-01-01 02:00:00 0.137190
3 1900-01-01 03:00:00 0.701568
4 1900-01-01 04:00:00 2.535264
Time Energy Generation (kW)
0 1900-01-01 00:00:00 0.0
1 1900-01-01 00:15:00 0.0
2 1900-01-01 00:30:00 0.0
3 1900-01-01 00:45:00 0.0
4 1900-01-01 01:00:00 0.0
%% Cell type:code id: tags:
``` python
merged = pd.merge(Usage,Generation,on='Time',how='left')
merged['Time'] = pd.to_datetime(merged['Time']).dt.strftime('%H:%M:%S')
merged
```
%%%% Output: execute_result
Time Energy Usage (kW) Energy Generation (kW)
0 00:00:00 0.001851 0.000000
1 01:00:00 0.018957 0.000000
2 02:00:00 0.137190 0.000000
3 03:00:00 0.701568 0.000000
4 04:00:00 2.535264 0.000000
5 05:00:00 6.474291 0.000000
6 06:00:00 11.684480 0.000000
7 07:00:00 14.908292 0.000000
8 08:00:00 13.473404 0.000000
9 09:00:00 8.729661 7.456000
10 10:00:00 4.409937 13.811556
11 11:00:00 2.714658 17.944889
12 12:00:00 3.655001 19.856000
13 13:00:00 6.607191 19.544889
14 14:00:00 10.927057 17.011556
15 15:00:00 15.576404 12.256000
16 16:00:00 19.036357 5.278222
17 17:00:00 19.938368 0.000000
18 18:00:00 17.896786 0.000000
19 19:00:00 13.767015 0.000000
20 20:00:00 9.075775 0.000000
21 21:00:00 5.127515 0.000000
22 22:00:00 2.482615 0.000000
23 23:00:00 1.030128 0.000000
%% Cell type:code id: tags:
``` python
merged.plot()
```
%%%% Output: execute_result
<matplotlib.axes._subplots.AxesSubplot at 0x11cad63d0>
%%%% Output: display_data
![](data:image/png;base64,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)
%% Cell type:code id: tags:
``` python
```
%% Cell type:markdown id: tags:
# Merging and Joining with Pandas
%% Cell type:markdown id: tags:
One of the tasks that needs to be mastered in manipulating data is to merge and join dataframes. This process may be familiar to those who have used `vlookup` in Excel to merge two spreadsheets. In `pandas` this can be performed using the `df.append()`, `pd.concat()` and `pd.merge()` functions. In this lesson we use two simple data sets to explain the basics of merging and joining with pandas.
%% Cell type:markdown id: tags:
## Contents
%% Cell type:markdown id: tags:
* Merging unordered dataframes
* Merging timeseries
* Exercises
%% Cell type:markdown id: tags:
## Merging unordered dataframes
%% Cell type:markdown id: tags:
The first data sets relate to the marks for a hypothetical Musicology unit at Ashmon University. We have ExamMark.csv with a set of ID numbers and exam marks out of 100. Similarly, there is CAMark.csv with ID numbers and continuous assessment mark out of 80. Then there is StudentNames.csv which contains the ID numbers and the full names of the students who enrolled in this unit at the beginning of semester. The student names are kept separate from the marks until the marks are finalised, since all marking has been done anonymously. There is a different range of student records in each file, since some dropped out before the end of semester or didn't complete the continuous assesment or exam. The exam and continuous assessment are each worth 50% of the final mark, and we want to create a new file which includes the names and marks for all students who completed the unit.
%% Cell type:code id: tags:
``` python
import pandas as pd
exams = pd.read_csv('ExamMark.csv')
ca = pd.read_csv('CAMark.csv')
studentnames = pd.read_csv('StudentNames.csv')
print(exams)
print(ca)
print(studentnames)
```
%%%% Output: stream
Student ID Exam Mark
0 7634 52
1 6789 73
2 9016 87
3 9532 81
4 8318 43
Student ID CA Mark
0 6789 62
1 7410 8
2 7634 34
3 9016 69
4 8318 65
ID Number Firstname Lastname
0 7634 James Brown
1 6789 Ella Fitzgerald
2 7410 Herbie Hancock
3 9016 Dolly Parton
4 9532 Keith Richards
5 2888 Thelonius Monk
%% Cell type:markdown id: tags:
The first problem that we can see is that there is a student with an exam mark and CA mark, but not listed in the student names. This is probably due to a late enrollment in the unit. Luckily we can look up these details and append this to the dataframe. The other problem is that we need the course code for each student, so we also need to append this to the dataframe. We will first examine how this can be done using `df.append()` and `pd.concat()`.
`df.append()` is just used for appending rows to a dataframe. The original dataframe and the new dataframe do not necessarily need to have the same columns. If they don't, the missing values will be filled with NaN.
We set `ignore_index=True` here so that the new dataframe recreates the indices. Otherwise the indices from the original dataframes are used, and there is the possibly of rows with the same indices.
%% Cell type:code id: tags:
``` python
newrow = pd.DataFrame({'ID Number':[8318, 2718],
'Firstname':['Nina', 'Chick'],
'Lastname':['Simone', 'Corea']})
studentnames.append([newrow], ignore_index=True)
```
%%%% Output: execute_result
ID Number Firstname Lastname
0 7634 James Brown
1 6789 Ella Fitzgerald
2 7410 Herbie Hancock
3 9016 Dolly Parton
4 9532 Keith Richards
5 2888 Thelonius Monk
6 8318 Nina Simone
7 2718 Chick Corea
%% Cell type:markdown id: tags:
The routine `pd.concat()` does the same type of joins of rows, but it also allows columns to be joined to form a new dataframe. The difference is now the dataframes that are to be merged must be specified as a list.
%% Cell type:code id: tags:
``` python
anotherrow = pd.DataFrame({'ID Number':[3141],'Firstname':['Dusty'],'Lastname':['Springfield']})
pd.concat([studentnames, newrow, anotherrow], ignore_index=True)
```
%%%% Output: execute_result
ID Number Firstname Lastname
0 7634 James Brown
1 6789 Ella Fitzgerald
2 7410 Herbie Hancock
3 9016 Dolly Parton
4 9532 Keith Richards
5 2888 Thelonius Monk
6 8318 Nina Simone
7 2718 Chick Corea
8 3141 Dusty Springfield
%% Cell type:markdown id: tags:
When merging the dataframes, we may want to add a `key` which is associated with the original dataframes. For example, if one set of student records corresponds to early enrollments, and the other to late enrollments, we can incorporate this by specifying keys. In this case we can keep the indices of the original dataframes.
%% Cell type:code id: tags:
``` python
pd.concat([studentnames, newrow], keys=['Early', 'Late'])
```
%%%% Output: execute_result
ID Number Firstname Lastname
Early 0 7634 James Brown
1 6789 Ella Fitzgerald
2 7410 Herbie Hancock
3 9016 Dolly Parton
4 9532 Keith Richards
5 2888 Thelonius Monk
Late 0 8318 Nina Simone
1 2718 Chick Corea
%% Cell type:markdown id: tags:
`pd.concat` also allows dataframes to have columns added by specifying `axis=1`. In this case the new dataframe is created so that rows with the same indices in the two original dataframes are used to create the new row. For example, consider concatenation of the `exams` and `ca` dataframes based on the indices.
%% Cell type:code id: tags:
``` python
print(exams)
print(ca)
pd.concat([exams,ca], axis=1)
```
%%%% Output: stream
Student ID Exam Mark
0 7634 52
1 6789 73
2 9016 87
3 9532 81
4 8318 43
Student ID CA Mark
0 6789 62
1 7410 8
2 7634 34
3 9016 69
4 8318 65
%%%% Output: execute_result
Student ID Exam Mark Student ID CA Mark
0 7634 52 6789 62
1 6789 73 7410 8
2 9016 87 7634 34
3 9532 81 9016 69
4 8318 43 8318 65
%% Cell type:markdown id: tags:
We now have two columns named `Student ID`. To correctly concatenate the two data frames we need to set the `Student ID` (which is a unique value in each row) as in the index in each dataframe, and then contenate based on these keys. This performs an 'outer join', which is equivalent to specifying `join='outer'`, and creates an entry for every index that occurs in either dataframe. The values that are missing are then filled with NaN.
%% Cell type:code id: tags:
``` python
newexams = exams.set_index('Student ID')
newca = ca.set_index('Student ID')
pd.concat([newexams, newca], axis=1)
```
%%%% Output: execute_result
Exam Mark CA Mark
Student ID
6789 73.0 62.0
7410 NaN 8.0
7634 52.0 34.0
8318 43.0 65.0
9016 87.0 69.0
9532 81.0 NaN
%% Cell type:markdown id: tags:
The alternative is to perform an 'inner join' by specifying `join='inner'` and then rows are only created if the indices occur in both dataframes.
%% Cell type:code id: tags:
``` python
pd.concat([newexams,newca], axis=1, join='inner')
```
%%%% Output: execute_result
Exam Mark CA Mark
Student ID
7634 52 34
6789 73 62
9016 87 69
8318 43 65
%% Cell type:markdown id: tags:
Now we will update `studentnames` so that it just includes the required missing rows.
%% Cell type:code id: tags:
``` python
newrow = pd.DataFrame({'ID Number': [8318],
'Firstname': ['Nina'],
'Lastname': ['Simone']})
studentnames = pd.concat([studentnames,newrow])
studentnames
```
%%%% Output: stream
ID Number Firstname Lastname
0 7634 James Brown
1 6789 Ella Fitzgerald
2 7410 Herbie Hancock
3 9016 Dolly Parton
4 9532 Keith Richards
5 2888 Thelonius Monk
0 8318 Nina Simone
%% Cell type:markdown id: tags:
We now need to add course codes to the dataframe. First we create a dataframe which has the course codes for each of our students.
%% Cell type:code id: tags:
``` python
coursecodes = pd.DataFrame([[9532,1],
[6789,1],
[2888,1],
[8318,2],
[7410,2],
[9016,3],
[7634,3]],
columns=['ID Number','Course Code'])
coursecodes
```
%%%% Output: execute_result
ID Number Course Code
0 9532 1
1 6789 1
2 2888 1
3 8318 2
4 7410 2
5 9016 3
6 7634 3
%% Cell type:markdown id: tags:
`pd.merge()` can be used to add the course codes to our student list. Note that `studentnames` and `coursecodes` have the common index 'ID Number'. Therefore if we merge them, by default this common index will be used to create the new dataframe.
%% Cell type:code id: tags:
``` python
studentnames = pd.merge(studentnames, coursecodes)
studentnames
```
%%%% Output: execute_result
ID Number Firstname Lastname Course Code
0 7634 James Brown 3
1 6789 Ella Fitzgerald 1
2 7410 Herbie Hancock 2
3 9016 Dolly Parton 3
4 9532 Keith Richards 1
5 2888 Thelonius Monk 1
6 8318 Nina Simone 2
%% Cell type:markdown id: tags:
We now want to merge the exams marks and the CA marks, which have the common index 'Student ID'. There are four ways we can do this by specifying the keyword `how`. 'left' merges are based on the rows of the left dataframe and if there are no corresponding values for that in right dataframe, these entries are filled with NaN. 'right' does the same, but based on the rows of the right data frame. 'inner' merges are based on the intersection of the two dataframes, i.e., the rows common to both. 'outer' merges are based on the union of the two data frames, i.e., rows that are in either or both dataframes. The default is the inner merge. The corresponding results for each merge for the marks are shown below.
Since the only common key in both dataframes is 'Student ID', the merges will be based on this. It doesn't need to be specified here, but we include it for demonstration. Here the left dataframe is 'exams', and the right dataframe is 'ca'.
%% Cell type:code id: tags:
``` python
# left merge
pd.merge(exams,ca,on='Student ID',how='left')