The course materials are maintained on github. The next lecture will discuss github in detail. Today, you'll get minimal instructions to get access to today's lecture materials.
We will start today with the interactive environment that we will be using often through the course: the Jupyter Notebook.
We will walk through the following steps together:
Download miniconda (be sure to get Version 3.6) and install it on your system (hopefully you have done this before coming to class)
Use the conda
command-line tool to update your package listing and install the IPython notebook:
Update conda
's listing of packages for your system:
$ conda update conda
Install IPython notebook and all its requirements
$ conda install jupyter notebook
Navigate to the directory containing the course material. For example:
$ cd LectureNotes/02_Procedural_Python
You should see a number of files in the directory, including these:
$ ls
Type jupyter notebook
in the terminal to start the notebook
$ jupyter notebook
If everything has worked correctly, it should automatically launch your default browser
Click on Lecture-Python-And-Data.ipynb
to open the notebook containing the content for this lecture.
With that, you're set up to use the Jupyter notebook!
a
is a list, the a.append(1)
adds 1
to the list.
In [71]:
# Integer arithematic
1 + 1
Out[71]:
In [72]:
# Integer division version floating point division
print (6 // 4, 6/ 4)
In [73]:
# Have the full set of "calculator functions" but need the numpy package
import numpy as np
print (6.0 * 3, np.sin(2*np.pi))
In [74]:
# Floats can have a null value called nan, not a number
a = np.nan
3*a
Out[74]:
In [75]:
# Can concatenate, substring, find, count, ...
In [76]:
a = "The lazy"
b = "brown fox"
print ("Concatenation: ", a + b)
print ("First three letters: " + a[0:3])
print ("Index of 'z': " + str(a.find('z')))
In [77]:
a_tuple = (1, 'ab', (1,2))
a_tuple
Out[77]:
In [78]:
a_tuple[2]
Out[78]:
In [79]:
a_list = [1, 'a', [1,2]]
In [80]:
a_list[0]
Out[80]:
In [81]:
a_list.append(2)
a_list
Out[81]:
In [82]:
a_list
Out[82]:
In [83]:
dir(a_list)
Out[83]:
In [84]:
help (a_list)
In [85]:
a_list.count(1)
Out[85]:
In [86]:
dessert_dict = {} # Empty dictionary
dessert_dict['Dave'] = "Cake"
dessert_dict["Joe"] = ["Cake", "Pie"]
print (dessert_dict)
In [87]:
dessert_dict["Dave"]
Out[87]:
In [88]:
# This produces an error
dessert_dict["Bernease"] = {}
dessert_dict
Out[88]:
In [89]:
dessert_dict["Bernease"] = {"Favorite": ["sorbet", "cobbler"], "Dislike": "Brownies"}
In [90]:
# A first name shell game
first_int = 1
second_int = first_int
second_int += 1
second_int
Out[90]:
In [91]:
# What is first_int?
first_int
Out[91]:
In [92]:
# A second name shell game
a_list = ['a', 'aa', 'aaa']
b_list = a_list
b_list.append('bb')
b_list
Out[92]:
In [93]:
# What is a_list?
a_list
Out[93]:
In [94]:
# Create a deep copy
import copy
# A second name shell game
a_list = ['a', 'aa', 'aaa']
b_list = copy.deepcopy(a_list)
b_list.append('bb')
print("b_list = %s" % str(b_list))
print("a_list = %s" % str(a_list))
Key insight: Deep vs. Shallow Copies
Resolving a name in the bash shell is done by searching the directories in the PATH
environment variable. The first executable with the name is run.
In [95]:
# Example 1 of name resolution in python
var = 10
def func(val):
var = val + 1
return val
In [96]:
# What is returned?
print("func(2) = %d" % func(2))
# What is var?
print("var = %d" % var)
In [97]:
# Example 2 of name resolution in python
var = 10
def func(val):
return val + var
In [98]:
# What is returned?
print("func(2) = %d" % func(2))
# What is var?
print("var = %d" % var)
Insights on python
name resolution
In [99]:
# A list and a dict are objects.
# dict has been implemented so that you see its values when you type
# the instance name.
# This is done with many python objects, like list.
a_dict = {'a': [1, 2], 'b': [3, 4, 5]}
a_dict
Out[99]:
In [100]:
# You access the data and methods (codes) associated with an object by
# using the "." operator. These are referred to collectively
# as attributes. Methods are followed by parentheses;
# values (properties) are not.
a_dict.keys()
Out[100]:
In [101]:
# You can discover the attributes of an object using "dir"
dir(a_dict)
Out[101]:
In addition to Python's built-in modules like the math
module we explored above, there are also many often-used third-party modules that are core tools for doing data science with Python.
Some of the most important ones are:
numpy
: Numerical PythonNumpy is short for "Numerical Python", and contains tools for efficient manipulation of arrays of data. If you have used other computational tools like IDL or MatLab, Numpy should feel very familiar.
scipy
: Scientific PythonScipy is short for "Scientific Python", and contains a wide range of functionality for accomplishing common scientific tasks, such as optimization/minimization, numerical integration, interpolation, and much more. We will not look closely at Scipy today, but we will use its functionality later in the course.
pandas
: Labeled Data Manipulation in PythonPandas is short for "Panel Data", and contains tools for doing more advanced manipulation of labeled data in Python, in particular with a columnar data structure called a Data Frame. If you've used the R statistical language (and in particular the so-called "Hadley Stack"), much of the functionality in Pandas should feel very familiar.
matplotlib
: Visualization in PythonMatplotlib started out as a Matlab plotting clone in Python, and has grown from there in the 15 years since its creation. It is the most popular data visualization tool currently in the Python data world (though other recent packages are starting to encroach on its monopoly).
Because the above packages are not included in Python itself, you need to install them separately. While it is possible to install these from source (compiling the C and/or Fortran code that does the heavy lifting under the hood) it is much easier to use a package manager like conda
. All it takes is to run
$ conda install numpy scipy pandas matplotlib
and (so long as your conda setup is working) the packages will be downloaded and installed on your system.
What are the elements of a table?
In [102]:
# Pandas DataFrames as table elements
import pandas as pd
What operations do we perform on tables?
In [103]:
df = pd.DataFrame({'A': [1,2,3], 'B': [2, 4, 6], 'ccc': [1.0, 33, 4]})
df
Out[103]:
In [104]:
sub_df = df[['A', 'ccc']]
sub_df
Out[104]:
In [105]:
df['A'] + 2*df['B']
Out[105]:
In [106]:
# Operations on a Pandas DataFrame
In [107]:
!ls
uncomment this to download the data:
In [108]:
#!curl -o pronto.csv https://data.seattle.gov/api/views/tw7j-dfaw/rows.csv?accessType=DOWNLOAD
Because we'll use it so much, we often import under a shortened name using the import ... as ...
pattern:
In [109]:
import pandas as pd
df = pd.read_csv('pronto.csv')
In [110]:
type(df)
Out[110]:
In [111]:
len(df)
Out[111]:
Now we can use the read_csv
command to read the comma-separated-value data:
Note: strings in Python can be defined either with double quotes or single quotes
The head()
and tail()
methods show us the first and last rows of the data
In [112]:
df.head()
Out[112]:
In [113]:
df.columns
Out[113]:
The shape
attribute shows us the number of elements:
In [114]:
df.shape
Out[114]:
The columns
attribute gives us the column names
The index
attribute gives us the index names
The dtypes
attribute gives the data types of each column:
In [115]:
df.dtypes
Out[115]:
Access columns by name using square-bracket indexing:
In [116]:
df_small = df[ 'stoptime']
In [117]:
type(df_small)
Out[117]:
In [118]:
df_small.tolist()
Out[118]:
Mathematical operations on columns happen element-wise:
In [119]:
trip_duration_hours = df['tripduration']/3600
trip_duration_hours[:3]
Out[119]:
In [120]:
df['trip_duration_hours'] = df['tripduration']/3600
In [121]:
del df['trip_duration_hours']
In [122]:
df.head()
Out[122]:
In [123]:
df.loc[[0,1],:]
Out[123]:
In [124]:
df_long_trips = df[df['tripduration'] >10000]
In [125]:
sel = df['tripduration'] >10000
df_long_trips = df[sel]
In [126]:
len(df)
Out[126]:
In [127]:
# Make a copy of a slice
df_subset = df[['starttime', 'stoptime']].copy()
df_subset['trip_hours'] = df['tripduration']/3600
Columns can be created (or overwritten) with the assignment operator. Let's create a tripminutes column with the number of minutes for each trip
More complicated mathematical operations can be done with tools in the numpy
package:
One trick to know when working with columns of times is that Pandas DateTimeIndex
provides a nice interface for working with columns of times.
For a dataset of this size, using pd.to_datetime
and specifying the date format can make things much faster (from the strftime reference, we see that the pronto data has format "%m/%d/%Y %I:%M:%S %p"
(Note: you can also use infer_datetime_format=True
in most cases to automatically infer the correct format, though due to a bug it doesn't work when AM/PM are present)
With it, we can extract, the hour of the day, the day of the week, the month, and a wide range of other views of the time:
Pandas includes an array of useful functionality for manipulating and analyzing tabular data. We'll take a look at two of these here.
The pandas.value_counts
returns statistics on the unique values within each column.
We can use it, for example, to break down rides by gender:
In [ ]:
#
Or to break down rides by age:
In [ ]:
#
By default, the values rather than the index are sorted. Use sort=False
to turn this behavior off:
In [ ]:
#
We can explore other things as well: day of week, hour of day, etc.
In [ ]:
#
One of the killer features of the Pandas dataframe is the ability to do group-by operations. You can visualize the group-by like this (image borrowed from the Python Data Science Handbook)
In [58]:
df.head()
Out[58]:
In [59]:
df_count = df.groupby(['from_station_id']).count()
df_count.head()
Out[59]:
In [60]:
ser_count = df_count['trip_id']
type(ser_count)
Out[60]:
In [61]:
ser_count.sort_values()
Out[61]:
In [62]:
df_count1 = df_count['trip_id']
df_count2 = df_count1.rename(columns={'trip_id': 'count'})
df_count2['new'] = 1
df_count2.head()
Out[62]:
In [63]:
df_mean = df.groupby(['from_station_id']).mean()
df_mean.head()
Out[63]:
In [64]:
dfgroup = df.groupby(['from_station_id'])
dfgroup.groups
Out[64]:
The simplest version of a groupby looks like this, and you can use almost any aggregation function you wish (mean, median, sum, minimum, maximum, standard deviation, count, etc.)
<data object>.groupby(<grouping values>).<aggregate>()
for example, we can group by gender and find the average of all numerical columns:
In [ ]:
It's also possible to indes the grouped object like it is a dataframe:
In [ ]:
You can even group by multiple values: for example we can look at the trip duration by time of day and by gender:
In [ ]:
The unstack()
operation can help make sense of this type of multiply-grouped data. What this technically does is split a multiple-valued index into an index plus columns:
In [ ]:
pandas
Of course, looking at tables of data is not very intuitive.
Fortunately Pandas has many useful plotting functions built-in, all of which make use of the matplotlib
library to generate plots.
Whenever you do plotting in the IPython notebook, you will want to first run this magic command which configures the notebook to work well with plots:
In [65]:
%matplotlib inline
Now we can simply call the plot()
method of any series or dataframe to get a reasonable view of the data:
In [66]:
import matplotlib.pyplot as plt
df['tripduration'].hist()
Out[66]:
In [ ]:
In [ ]:
For example, we can create a histogram of trip durations:
In [ ]:
If you'd like to adjust the x and y limits of the plot, you can use the set_xlim()
and set_ylim()
method of the resulting object:
In [ ]:
In [ ]:
Split this plot by gender. Do you see any seasonal ridership patterns by gender?
In [ ]:
Split this plot by user type. Do you see any seasonal ridership patterns by usertype?
In [ ]:
Repeat the above three steps, counting the number of rides by time of day rather thatn by month.
In [ ]:
Are there any other interesting insights you can discover in the data using these tools?
In [ ]:
In [67]:
# A script for creating a dataframe with counts of the occurrence of a columns' values
df_count = df.groupby('from_station_id').count()
df_count1 = df_count[['trip_id']]
df_count2 = df_count1.rename(columns={'trip_id': 'count'})
In [68]:
df_count2.head()
Out[68]:
In [69]:
def make_table_count(df_arg, groupby_column):
df_count = df_arg.groupby(groupby_column).count()
column_name = df.columns[0]
df_count1 = df_count[[column_name]]
df_count2 = df_count1.rename(columns={column_name: 'count'})
return df_count2
In [70]:
dff = make_table_count(df, 'from_station_id')
dff.head()
Out[70]: