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

ADS1002 - week 7 errors and explanations notebook.

parent 97a8800a
%% Cell type:markdown id:55078a6a tags:
# Manipulating DataFrames
Some examples to help understand common errors in dataframes, and the difference between a dataframe and a series.
%% Cell type:code id:2b33c47d tags:
``` python
# Starting with a simple example dataset we've seen before: the iris dataset.
import pandas as pd
import seaborn as sns
iris_dataframe = sns.load_dataset("iris")
iris_dataframe.sample(10)
```
%%%% Output: execute_result
sepal_length sepal_width petal_length petal_width species
78 6.0 2.9 4.5 1.5 versicolor
117 7.7 3.8 6.7 2.2 virginica
88 5.6 3.0 4.1 1.3 versicolor
30 4.8 3.1 1.6 0.2 setosa
72 6.3 2.5 4.9 1.5 versicolor
60 5.0 2.0 3.5 1.0 versicolor
135 7.7 3.0 6.1 2.3 virginica
41 4.5 2.3 1.3 0.3 setosa
75 6.6 3.0 4.4 1.4 versicolor
56 6.3 3.3 4.7 1.6 versicolor
%% Cell type:code id:2f3d77a9 tags:
``` python
# Q1 -> what's the result of this command?
# A -> This gives a series (single column).
# Note that a Dataframe is collection of series (one for each column)
# which are aligned on the same index.
iris_dataframe['sepal_length']
```
%%%% Output: execute_result
0 5.1
1 4.9
2 4.7
3 4.6
4 5.0
...
145 6.7
146 6.3
147 6.5
148 6.2
149 5.9
Name: sepal_length, Length: 150, dtype: float64
%% Cell type:code id:41f4ec0a tags:
``` python
# Q2 -> what's the result of this command?
# A -> This gives a two-column dataframe.
iris_dataframe[['sepal_length', 'sepal_width']]
```
%%%% Output: execute_result
sepal_length sepal_width
0 5.1 3.5
1 4.9 3.0
2 4.7 3.2
3 4.6 3.1
4 5.0 3.6
.. ... ...
145 6.7 3.0
146 6.3 2.5
147 6.5 3.0
148 6.2 3.4
149 5.9 3.0
[150 rows x 2 columns]
%% Cell type:code id:ae132736 tags:
``` python
# Q3 -> what's the result of this command?
# A -> This gives one two-column dataframe.
# Notes: in general this column-selection syntax can be broken down as:
# iris_dataframe[..something...]
# The outer set of square brackets indicate that we are selecting some data.
# In python this is referred to as slicing.
# If 'something' is just one string, we get back one column as a series.
# If 'something' is a list of strings, we get back a dataframe with the
# selected columns.
# If this list has only one entry, we still get a dataframe, it just has
# only one column.
# So, the two square brackets [[...]] mean we are selecting with a list of
# length one.
iris_dataframe[['sepal_length']]
```
%%%% Output: execute_result
sepal_length
0 5.1
1 4.9
2 4.7
3 4.6
4 5.0
.. ...
145 6.7
146 6.3
147 6.5
148 6.2
149 5.9
[150 rows x 1 columns]
%% Cell type:code id:f34c0121 tags:
``` python
# Q4 -> what's the result of this command?
# A -> KeyError. KeyError is common for lookup errors, we couldn't find the
# column name. Note the error message 'not in index'; pandas considers both
# the index (row labels) and columns (column labels) as 'indexes' of some sort,
# so an error message 'not in index' may indicate a failed column lookup.
iris_dataframe[['sepal_lingth', 'sepal_width']]
```
%%%% Output: error
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
/tmp/ipykernel_9573/2938404256.py in <module>
4 # the index (row labels) and columns (column labels) as 'indexes' of some sort,
5 # so an error message 'not in index' may indicate a failed column lookup.
----> 6 iris_dataframe[['sepal_lingth', 'sepal_width']]
~/.pyenv/versions/3.9.6/lib/python3.9/site-packages/pandas/core/frame.py in __getitem__(self, key)
3459 if is_iterator(key):
3460 key = list(key)
-> 3461 indexer = self.loc._get_listlike_indexer(key, axis=1)[1]
3462
3463 # take() does not accept boolean indexers
~/.pyenv/versions/3.9.6/lib/python3.9/site-packages/pandas/core/indexing.py in _get_listlike_indexer(self, key, axis)
1312 keyarr, indexer, new_indexer = ax._reindex_non_unique(keyarr)
1313
-> 1314 self._validate_read_indexer(keyarr, indexer, axis)
1315
1316 if needs_i8_conversion(ax.dtype) or isinstance(
~/.pyenv/versions/3.9.6/lib/python3.9/site-packages/pandas/core/indexing.py in _validate_read_indexer(self, key, indexer, axis)
1375
1376 not_found = list(ensure_index(key)[missing_mask.nonzero()[0]].unique())
-> 1377 raise KeyError(f"{not_found} not in index")
1378
1379
KeyError: "['sepal_lingth'] not in index"
%% Cell type:code id:8991f60a tags:
``` python
# Note that we still get KeyErrors in the next two cases, but sometimes
# the error message is slightly different (in this case, it is actually
# more informative!).
iris_dataframe[['sepal_lingth', 'sepal_wedth']]
```
%% Cell type:code id:49073fc1 tags:
``` python
# Another error... one thing to note: you can mostly ignore the initial part
# of the error message which refers to pandas internal code. This is called
# a stack trace. In some cases it will be useful in helping you debug your own
# code, but for simple one-line operations like this, the stack trace just
# looks at pandas internal code. To figure out what has gone wrong in this case
# focus on the error message itself (right at the bottom of all this output).
iris_dataframe[['sepal_lingth', 'sepal_width']]
```
%% Cell type:code id:b9370647 tags:
``` python
# This piece of code is a bit contrived, but here is a case where looking
# at the stack trace is helpful.
# The very first part of the trace refers to the code in our notebook cell:
#
# 1 iris_dataframe[['sepal_length', 'sepal_width']]
# 2 iris_dataframe[['petal_length', 'sepal_width']]
# ----> 3 iris_dataframe[['sepal_lingth', 'sepal_width']]
# 4 iris_dataframe[['sepal_length', 'petal_width']]
# 5 iris_dataframe[['sepal_length', 'petal_length']]
#
# This indicates that the error originated from the 3rd line of our code.
iris_dataframe[['sepal_length', 'sepal_width']]
iris_dataframe[['petal_length', 'sepal_width']]
iris_dataframe[['sepal_lingth', 'sepal_width']]
iris_dataframe[['sepal_length', 'petal_width']]
iris_dataframe[['sepal_length', 'petal_length']]
```
%%%% Output: error
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
/tmp/ipykernel_9573/774806270.py in <module>
13 iris_dataframe[['sepal_length', 'sepal_width']]
14 iris_dataframe[['petal_length', 'sepal_width']]
---> 15 iris_dataframe[['sepal_lingth', 'sepal_width']]
16 iris_dataframe[['sepal_length', 'petal_width']]
17 iris_dataframe[['sepal_length', 'petal_length']]
~/.pyenv/versions/3.9.6/lib/python3.9/site-packages/pandas/core/frame.py in __getitem__(self, key)
3459 if is_iterator(key):
3460 key = list(key)
-> 3461 indexer = self.loc._get_listlike_indexer(key, axis=1)[1]
3462
3463 # take() does not accept boolean indexers
~/.pyenv/versions/3.9.6/lib/python3.9/site-packages/pandas/core/indexing.py in _get_listlike_indexer(self, key, axis)
1312 keyarr, indexer, new_indexer = ax._reindex_non_unique(keyarr)
1313
-> 1314 self._validate_read_indexer(keyarr, indexer, axis)
1315
1316 if needs_i8_conversion(ax.dtype) or isinstance(
~/.pyenv/versions/3.9.6/lib/python3.9/site-packages/pandas/core/indexing.py in _validate_read_indexer(self, key, indexer, axis)
1375
1376 not_found = list(ensure_index(key)[missing_mask.nonzero()[0]].unique())
-> 1377 raise KeyError(f"{not_found} not in index")
1378
1379
KeyError: "['sepal_lingth'] not in index"
%% Cell type:markdown id:90dce1de tags:
# Types
%% Cell type:code id:0783d68c tags:
``` python
iris_dataframe.dtypes
```
%%%% Output: execute_result
sepal_length float64
sepal_width float64
petal_length float64
petal_width float64
species object
dtype: object
%% Cell type:markdown id:9a38303b tags:
**Question**: if there are nan values would the datatype be 'object'?
**Answer**: not necessarily, numeric columns can still have NaN values. More specifically:
* Floating-point (type = 'float') columns can have NaN values.
* An integer column **cannot** have NaN values. If you include NaN values in an integer column the entire column is (automatically) turned into floating point. This is the result of some internal implementation in pandas.
* Object columns typically indicate string values, a common example is un-converted datetimes read from csv. Use pd.to_datetime to convert these to pandas' native type so that they are handled properly.
%% Cell type:code id:6c071566 tags:
``` python
# There is also a categorical type conversion we can do:
iris_dataframe["species"].astype('category')
```
%%%% Output: execute_result
0 setosa
1 setosa
2 setosa
3 setosa
4 setosa
...
145 virginica
146 virginica
147 virginica
148 virginica
149 virginica
Name: species, Length: 150, dtype: category
Categories (3, object): ['setosa', 'versicolor', 'virginica']
%% Cell type:markdown id:cc07ad2e tags:
# Plots
Similar errors occur when using pandas + seaborn.
%% Cell type:code id:51755d1b tags:
``` python
# Q5 -> what's the result of this command?
# A -> a scatter plot (this is the default for relplot). In this case
# the function call indicates that the plot data should be sourced from
# the 'iris_dataframe' dataframe. This means all columns referred to must
# be present in this dataframe. Here the x/y position of points comes
# from sepal_width/sepal_length, and the category colour comes from the
# species column. Hence we get 3 unique colours.
sns.relplot(
data=iris_dataframe, # source dataframe
x="sepal_width",
y="sepal_length",
hue='species',
)
```
%%%% Output: execute_result
<seaborn.axisgrid.FacetGrid at 0x7fa44d9faa30>
%%%% Output: display_data
![](data:image/png;base64,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)
%% Cell type:code id:2ea6bc0e tags:
``` python
# Q6 -> what's the result of this command?
# A -> ValueError. We mis-spelled a column, but with seaborn we get
# a ValueError instead of a KeyError. Why? I'm not really sure, it's
# just a different choice made by the developers of seaborn vs. pandas.
# But the take-home message is: ignore the error type. Look for the
# first ----> in the stack trace to figure out which line of your code
# the error came from, and read the final error message carefully.
sns.relplot(
data=iris_dataframe,
x="sepal_wodth",
y="sepal_length",
hue='species',
)
```
%%%% Output: error
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
/tmp/ipykernel_9573/3922716469.py in <module>
6 # first ----> in the stack trace to figure out which line of your code
7 # the error came from, and read the final error message carefully.
----> 8 sns.relplot(
9 data=iris_dataframe,
10 x="sepal_wodth",
~/.pyenv/versions/3.9.6/lib/python3.9/site-packages/seaborn/_decorators.py in inner_f(*args, **kwargs)
44 )
45 kwargs.update({k: arg for k, arg in zip(sig.parameters, args)})
---> 46 return f(**kwargs)
47 return inner_f
48
~/.pyenv/versions/3.9.6/lib/python3.9/site-packages/seaborn/relational.py in relplot(x, y, hue, size, style, data, row, col, col_wrap, row_order, col_order, palette, hue_order, hue_norm, sizes, size_order, size_norm, markers, dashes, style_order, legend, kind, height, aspect, facet_kws, units, **kwargs)
945
946 # Use the full dataset to map the semantics
--> 947 p = plotter(
948 data=data,
949 variables=plotter.get_semantics(locals()),
~/.pyenv/versions/3.9.6/lib/python3.9/site-packages/seaborn/relational.py in __init__(self, data, variables, x_bins, y_bins, estimator, ci, n_boot, alpha, x_jitter, y_jitter, legend)
585 )
586
--> 587 super().__init__(data=data, variables=variables)
588
589 self.alpha = alpha
~/.pyenv/versions/3.9.6/lib/python3.9/site-packages/seaborn/_core.py in __init__(self, data, variables)
603 def __init__(self, data=None, variables={}):
604
--> 605 self.assign_variables(data, variables)
606
607 for var, cls in self._semantic_mappings.items():
~/.pyenv/versions/3.9.6/lib/python3.9/site-packages/seaborn/_core.py in assign_variables(self, data, variables)
666 else:
667 self.input_format = "long"
--> 668 plot_data, variables = self._assign_variables_longform(
669 data, **variables,
670 )
~/.pyenv/versions/3.9.6/lib/python3.9/site-packages/seaborn/_core.py in _assign_variables_longform(self, data, **kwargs)
901
902 err = f"Could not interpret value `{val}` for parameter `{key}`"
--> 903 raise ValueError(err)
904
905 else:
ValueError: Could not interpret value `sepal_wodth` for parameter `x`
%% Cell type:markdown id:b141373e tags:
# KeyError vs ValueError
**Question**: What's the difference between key error and value error?
**Answer**: All python errors have types. Typically KeyError means "you
tried to look something up, but I couldn't find it" and ValueError means
"you gave me some input I don't know how to deal with". More important
that the type, though, is the error message itself, which should give you
a clue as to the root cause of the error.
%% Cell type:markdown id:b6f67b8a tags:
# Split, Fit, Predict, Evaluate
%% Cell type:code id:4f788487 tags:
``` python
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, precision_score, recall_score
```
%% Cell type:code id:46226fe1 tags:
``` python
# Gives a series of true/false values. Specifically, we extract the 'species'
# column and compare every entry to the string 'versicolor'.
iris_dataframe['species'] == 'versicolor'
```
%%%% Output: execute_result
0 False
1 False
2 False
3 False
4 False
...
145 False
146 False
147 False
148 False
149 False
Name: species, Length: 150, dtype: bool
%% Cell type:code id:1f0a9108 tags:
``` python
# From the Logistic Regression notebook.
# Here we create another column using the boolean column from the previous cell.
iris_dataframe['is_versicolor'] = (iris_dataframe['species'] == 'versicolor')
# We then convert it to a 0/1 value (integer type). This allowed me to plot
# the classification on the y-axis in the examples.
iris_dataframe['is_versicolor'] = iris_dataframe['is_versicolor'].astype(int)
# Select 10 random columns. Otherwise we'll only see the top and bottom 10 values.
# None of these are 'versicolor' ... so my fancy new column would appear
# to be all zeroes!
iris_dataframe.sample(10)
```
%%%% Output: execute_result
sepal_length sepal_width petal_length petal_width species \
49 5.0 3.3 1.4 0.2 setosa
97 6.2 2.9 4.3 1.3 versicolor
118 7.7 2.6 6.9 2.3 virginica
58 6.6 2.9 4.6 1.3 versicolor
141 6.9 3.1 5.1 2.3 virginica
104 6.5 3.0 5.8 2.2 virginica
50 7.0 3.2 4.7 1.4 versicolor
134 6.1 2.6 5.6 1.4 virginica
139 6.9 3.1 5.4 2.1 virginica
120 6.9 3.2 5.7 2.3 virginica
is_versicolor
49 0
97 1
118 0
58 1
141 0
104 0
50 1
134 0
139 0
120 0
%% Cell type:code id:e524f0ca tags:
``` python
# Just to verify: our dataset now has 150 rows and 6 columns.
print(iris_dataframe.shape)
iris_dataframe.head()
```
%%%% Output: execute_result
sepal_length sepal_width petal_length petal_width species is_versicolor
0 5.1 3.5 1.4 0.2 setosa 0
1 4.9 3.0 1.4 0.2 setosa 0
2 4.7 3.2 1.3 0.2 setosa 0
3 4.6 3.1 1.5 0.2 setosa 0
4 5.0 3.6 1.4 0.2 setosa 0
%% Cell type:code id:d0969fdb tags:
``` python
# A not very helpful help message, indicating that we should pass this
# function a 'sequence of indexables'. This is because sklearn functions
# have more generalist capabilities than what we have seen so far.
help(train_test_split)
```
%% Cell type:code id:8d03ffde tags:
``` python
# Q7 - how many rows and columns in X_train & y_train? 100 rows, 2 & 1 columns respectively
# Q8 - how many rows and columns in X_test & y_test? 50 rows, 2 & 1 columns respectively
X_train, X_test, y_train, y_test = train_test_split(
iris_dataframe[['sepal_width', 'sepal_length']], # 2 column, feature values
iris_dataframe['is_versicolor'], # target value (series, one column)
test_size=0.33, # one third of the data (50 rows) should go into the test set
random_state=45, # seed value: same seed value --> same split every time
)
X_train
```
%%%% Output: execute_result
sepal_width sepal_length
45 3.0 4.8
66 3.0 5.6
128 2.8 6.4
48 3.7 5.3
144 3.3 6.7
.. ... ...
68 2.2 6.2
95 3.0 5.7
32 4.1 5.2
124 3.3 6.7
131 3.8 7.9
[100 rows x 2 columns]
%% Cell type:code id:3b155d37 tags:
``` python
# Using fit-predict: follow the outputs of train_test_split
model = LogisticRegression()
model.fit(X_train, y_train) # features values and corresponding true target values
model.predict(X_test) # feature values only -> returns predicted target values
```
%%%% Output: execute_result
array([0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
1, 0, 1, 1, 0, 0])
%% Cell type:code id:bd5e68a0 tags:
``` python
# An error: we passed predict some columns that it couldn't deal with
# (since we never fitted the model to all columns, only 'sepal_width'
# and 'sepal_length'
model.predict(iris_dataframe)
```
%%%% Output: error
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
/tmp/ipykernel_9573/331035052.py in <module>
2 # (since we never fitted the model to all columns, only 'sepal_width'
3 # and 'sepal_length'
----> 4 model.predict(iris_dataframe)
~/.pyenv/versions/3.9.6/lib/python3.9/site-packages/sklearn/linear_model/_base.py in predict(self, X)
307 Predicted class label per sample.
308 """
--> 309 scores = self.decision_function(X)
310 if len(scores.shape) == 1:
311 indices = (scores > 0).astype(int)
~/.pyenv/versions/3.9.6/lib/python3.9/site-packages/sklearn/linear_model/_base.py in decision_function(self, X)
282 check_is_fitted(self)
283
--> 284 X = check_array(X, accept_sparse='csr')
285
286 n_features = self.coef_.shape[1]
~/.pyenv/versions/3.9.6/lib/python3.9/site-packages/sklearn/utils/validation.py in inner_f(*args, **kwargs)
61 extra_args = len(args) - len(all_args)
62 if extra_args <= 0:
---> 63 return f(*args, **kwargs)
64
65 # extra_args > 0
~/.pyenv/versions/3.9.6/lib/python3.9/site-packages/sklearn/utils/validation.py in check_array(array, accept_sparse, accept_large_sparse, dtype, order, copy, force_all_finite, ensure_2d, allow_nd, ensure_min_samples, ensure_min_features, estimator)
671 array = array.astype(dtype, casting="unsafe", copy=False)
672 else:
--> 673 array = np.asarray(array, order=order, dtype=dtype)
674 except ComplexWarning as complex_warning:
675 raise ValueError("Complex data not supported\n"
~/.pyenv/versions/3.9.6/lib/python3.9/site-packages/pandas/core/generic.py in __array__(self, dtype)
1991
1992 def __array__(self, dtype: NpDtype | None = None) -> np.ndarray:
-> 1993 return np.asarray(self._values, dtype=dtype)
1994
1995 def __array_wrap__(
ValueError: could not convert string to float: 'setosa'
%% Cell type:code id:1844e841 tags:
``` python
# Q9 - what happens here, and why?
# A: we passed the wrong set of target values. The error indicates a length mismatch.
model = LogisticRegression()
model.fit(X_train, y_test)
```
%%%% Output: error