Lambda School Data Science

*Unit 2, Sprint 3, Module 1*

---

# Define ML problems

You will use your portfolio project dataset for all assignments this sprint.

## Assignment

Complete these tasks for your project, and document your decisions.

- [ ] Choose your target. Which column in your tabular dataset will you predict?
- [ ] Is your problem regression or classification?
- [ ] How is your target distributed?
    - Classification: How many classes? Are the classes imbalanced?
    - Regression: Is the target right-skewed? If so, you may want to log transform the target.
- [ ] Choose which observations you will use to train, validate, and test your model.
    - Are some observations outliers? Will you exclude them?
    - Will you do a random split or a time-based split?
- [ ] Choose your evaluation metric(s).
    - Classification: Is your majority class frequency > 50% and < 70% ? If so, you can just use accuracy if you want. Outside that range, accuracy could be misleading. What evaluation metric will you choose, in addition to or instead of accuracy?
- [ ] Begin to clean and explore your data.
- [ ] Begin to choose which features, if any, to exclude. Would some features "leak" future information?

```python
import pandas as pd
adoption_url = 'https://data.austintexas.gov/resource/9t4d-g238.csv?$limit=100000'
adoption = pd.read_csv(adoption_url)
# in order to see all of the columns:
pd.options.display.max_columns = 100
```

# Target

```python
adoption.head()
```

      animal_id
      name
      datetime
      monthyear
      date_of_birth
      outcome_type
      outcome_subtype
      animal_type
      sex_upon_outcome
      age_upon_outcome
      breed
      color

      0
      A808320
      Kyha
      2019-11-14T15:37:00.000
      2019-11-14T15:37:00.000
      2017-11-07T00:00:00.000
      Rto-Adopt
      NaN
      Dog
      Spayed Female
      2 years
      German Shepherd Mix
      Sable

      1
      A781697
      Pookie
      2019-11-14T15:33:00.000
      2019-11-14T15:33:00.000
      2017-10-05T00:00:00.000
      Adoption
      NaN
      Dog
      Spayed Female
      2 years
      Cairn Terrier
      White/Brown

      2
      A808382
      NaN
      2019-11-14T14:57:00.000
      2019-11-14T14:57:00.000
      2014-11-08T00:00:00.000
      Transfer
      Partner
      Cat
      Intact Male
      5 years
      Domestic Shorthair
      Orange Tabby

      3
      A806701
      *Emerald
      2019-11-14T14:41:00.000
      2019-11-14T14:41:00.000
      2019-09-02T00:00:00.000
      Adoption
      Foster
      Cat
      Neutered Male
      2 months
      Domestic Shorthair
      Blue Tabby/White

      4
      A804553
      *Wendy
      2019-11-14T14:36:00.000
      2019-11-14T14:36:00.000
      2019-08-09T00:00:00.000
      Adoption
      Foster
      Cat
      Spayed Female
      3 months
      Domestic Shorthair
      Calico

```python
adoption.shape
```

    (100000, 12)

The target in this project will be to predict whether or not an animal will be adopted or not (transferred to another shelter or, sadly, euthanized) so that perhaps animal shelters, though overwhelmed, can give some extra love or use unique methods to get those animals that may not have the best odds forever homes.

```python
adoption['outcome_type'].value_counts(dropna=False)
```

    Adoption           44390
    Transfer           29861
    Return to Owner    17510
    Euthanasia          6294
    Died                 969
    Rto-Adopt            508
    Disposal             387
    Missing               61
    Relocate              17
    NaN                    3
    Name: outcome_type, dtype: int64

```python
# this might be a feature that can create leakage, will come back to it.
adoption['outcome_subtype'].value_counts(dropna=False)
```

    NaN                    54837
    Partner                24943
    Foster                  7953
    Rabies Risk             2789
    SCRP                    2636
    Suffering               2503
    Snr                     2279
    In Kennel                508
    Aggressive               358
    Offsite                  281
    Medical                  254
    In Foster                243
    At Vet                   171
    Behavior                  82
    Enroute                   65
    Underage                  29
    Court/Investigation       21
    In Surgery                19
    Possible Theft            15
    Field                      8
    Barn                       4
    Customer S                 1
    Prc                        1
    Name: outcome_subtype, dtype: int64

# Classification or Regression?

There are 9 classes of outcomes but for this project I'd like to focus on the animals that were adopted or not. There are a large number of animals that were returned to owners but that would just be due to them getting out etc but they do have a home so I will not include those in my project. "Rto-adopt" or return to owner adoption will also be included with "return to owner."

For animals that were adopted I will consider that to be:
-adoption
-rto-adopt (return to owner through adoption)

I will combine the following for not adopted:
-transfer
-euthanasia
-relocate
-missing (animals that went missing from the shelter--still unsuccessful in getting them homes)
"Died" and "disposal" are animals that may have died while at the shelter or were brought in that needed to be properly disposed of so I will not include these either as they may have been very ill when brought in.

# How is the target distributed?
## Are the classes imbalanced?

```python
adoption['outcome_type'].value_counts(normalize=True)
```

    Adoption           0.443913
    Transfer           0.298619
    Return to Owner    0.175105
    Euthanasia         0.062942
    Died               0.009690
    Rto-Adopt          0.005080
    Disposal           0.003870
    Missing            0.000610
    Relocate           0.000170
    Name: outcome_type, dtype: float64

will need to drop the rows where the outcome type are the ones listed above to be excluded:
-Return to owner
-Rto-adopt
-Died
-Disposal

```python
# adoption updated to drop the outcomes we are excluding:
adoption_upd = adoption[~adoption['outcome_type'].isin(['Return to Owner',
                                                        'Rto-Adopt',
                                                        'Died',
                                                        'Disposal'])]
```

```python
adoption_upd['outcome_type'].value_counts(dropna=False)
```

    Adoption      44390
    Transfer      29861
    Euthanasia     6294
    Missing          61
    Relocate         17
    NaN               3
    Name: outcome_type, dtype: int64

```python
# am going to drop all animals except cats and dogs, 2 of the unknown
# outcome types are dogs and 1 also has a lot of other missing info so
# will drop these rows.
adoption_upd = adoption_upd.dropna(subset = ['outcome_type'])

```

```python
# need to redefine classes as binary. Adoption as 'adopted' and
# the rest as 'not adopted'.
def new_status(outcome):
    if outcome == 'Transfer' or outcome == 'Euthanasia' or outcome == 'Missing' or outcome == 'Relocate':
      return 'Not adopted'
    else:
      return 'Adopted'

```

```python
adoption_upd = adoption_upd.copy()
adoption_upd['new_outcome_type'] = adoption_upd['outcome_type'].apply(new_status)
```

```python
adoption_upd['new_outcome_type'].value_counts(normalize=True)
```

    Adopted        0.550587
    Not adopted    0.449413
    Name: new_outcome_type, dtype: float64

The classes now are combined into a binary classification and the classes are not imbalanced.

```python
# drop the original 'Outcome_Type' column:
adoption_upd = adoption_upd.drop(columns='outcome_type')
```

# Choose Observations

As mentioned above, since the focus of my project is predicting if animals that are in need of forever homes will be adopted or not, I have already excluded the following observations from my model:
-Return to Owner
-Rto-Adopt
-Died
-Disposal

There are 3 missing values for the outcome ,so we can try to look at the outcome subtype to see if we can determine what happened to them but the remaining animals have been categorized into 'adopted' or 'not adopted.'

# How to Split Data:

```python
adoption_upd.dtypes
```

    animal_id           object
    name                object
    datetime            object
    monthyear           object
    date_of_birth       object
    outcome_subtype     object
    animal_type         object
    sex_upon_outcome    object
    age_upon_outcome    object
    breed               object
    color               object
    new_outcome_type    object
    dtype: object

```python
adoption_upd['datetime'] = pd.to_datetime(adoption_upd['datetime'], infer_datetime_format=True)
```

```python
adoption_upd['datetime'].dt.year.value_counts()
```

    2015    14792
    2019    14373
    2016    14119
    2017    14058
    2018    13361
    2014     9920
    Name: datetime, dtype: int64

The description of the data set said that Austin is becoming a more pet-friendly city so there may be more animals going in and out of shelters in the more recent data vs the earlier data. I will therefore split the data based on time with the most recent data being the test set and then create a test and validation set with the remaining data.

test = adoption_upd[(adoption_upd['datetime'].dt.year == 2019)]
val =  adoption_upd[(adoption_upd['datetime'].dt.year == 2018)]
train = adoption_upd[(adoption_upd['datetime'].dt.year < 2018)]

```python
# how big to make test set? 2019:
# 14292 observations
```

# Evaluation Metrics

since the classes aren't imbalanced I can use accuracy but will also explore the precision and recall for this problem.

precision positive: correctly predict all the animals that were adopted.

\begin{align}
precision = \frac{accurately \ predicted \ adopted}{total\ predicted \ adopted}
\end{align}

recall positive: of all the animals that were adopted, how many were we able to identify?
\begin{align}
recall = \frac{accurately \ predicted \ adopted}{actually \ adopted}
\end{align}

# Begin to clean data and feature selection

```python
adoption_upd.isnull().sum()
```

    animal_id               0
    name                30056
    datetime                0
    monthyear               0
    date_of_birth           0
    outcome_subtype     36351
    animal_type             0
    sex_upon_outcome        2
    age_upon_outcome       24
    breed                   0
    color                   0
    new_outcome_type        0
    dtype: int64

```python
# the name column has a lot of missing values, will change NaN's to Unknown
adoption_upd['name'].fillna("Unknown", inplace = True)
```

```python
# the outcome subtype is missing 36353 values, almost half of all of our
# data, since we will know all of the outcome types and this may cause
# leakage into the test set because certain outcomes can be deduced from
# the outcome subtype, I will drop that entire column.

adoption_upd = adoption_upd.drop(columns='outcome_subtype')
adoption_upd.shape

```

    (80623, 11)

```python
# the sex_upon_outcome column has 2 missing values but noticed an 'unknown'
# value so check to see if that's common:
adoption_upd['sex_upon_outcome'].value_counts()
# will one hot encode this column.
```

    Neutered Male    27561
    Spayed Female    26019
    Intact Female    10098
    Intact Male       9372
    Unknown           7571
    Name: sex_upon_outcome, dtype: int64

```python
# age_upon_outcome has 25 missing values but date_of_birth has none, lets
# look at how many times 'unknown' shows up in the data:
adoption_upd.isin(['Unknown']).sum()
# the name already has 18 unknowns, so will stick to changing NaN's to unknown.
```

    animal_id               0
    name                30074
    datetime                0
    monthyear               0
    date_of_birth           0
    animal_type             0
    sex_upon_outcome     7571
    age_upon_outcome        0
    breed                   0
    color                   0
    new_outcome_type        0
    dtype: int64

```python
# to see if 'Other is a common entry as well'
adoption_upd.isin(['Other']).sum()
```

    animal_id              0
    name                   0
    datetime               0
    monthyear              0
    date_of_birth          0
    animal_type         4450
    sex_upon_outcome       0
    age_upon_outcome       0
    breed                  0
    color                  0
    new_outcome_type       0
    dtype: int64

```python
# to see what kind of animals come in to the shelter
adoption_upd['animal_type'].value_counts()
```

    Dog          39758
    Cat          35974
    Other         4450
    Bird           432
    Livestock        9
    Name: animal_type, dtype: int64

```python
# initially thinking of only using cats and dogs.
adoption_upd = adoption_upd[~adoption_upd['animal_type'].isin(['Bird',
                                                        'Other',
                                                        'Livestock'])]
```

```python
# check for missing values now:
adoption_upd.isnull().sum()
```

    animal_id           0
    name                0
    datetime            0
    monthyear           0
    date_of_birth       0
    animal_type         0
    sex_upon_outcome    2
    age_upon_outcome    8
    breed               0
    color               0
    new_outcome_type    0
    dtype: int64

```python
adoption_upd['date_of_birth'] = pd.to_datetime(adoption_upd['date_of_birth'], infer_datetime_format=True)
```

```python
adoption_upd.loc[adoption_upd['age_upon_outcome'].isnull()]
```

      animal_id
      name
      datetime
      monthyear
      date_of_birth
      animal_type
      sex_upon_outcome
      age_upon_outcome
      breed
      color
      new_outcome_type

      59
      A808636
      Unknown
      2019-11-13 13:46:00
      2019-11-13T13:46:00.000
      2004-11-11
      Cat
      Intact Female
      NaN
      Siamese
      Seal Point
      Not adopted

      96
      A808702
      Keepers
      2019-11-12 15:20:00
      2019-11-12T15:20:00.000
      2009-11-12
      Dog
      Neutered Male
      NaN
      Golden Retriever
      Gold
      Not adopted

      131
      A808649
      Unknown
      2019-11-11 17:49:00
      2019-11-11T17:49:00.000
      2019-10-11
      Cat
      Intact Male
      NaN
      Domestic Shorthair
      Brown Tabby
      Not adopted

      132
      A738697
      Boots
      2019-11-11 17:48:00
      2019-11-11T17:48:00.000
      2007-11-17
      Dog
      NaN
      NaN
      Miniature Schnauzer Mix
      Black
      Not adopted

      161
      A808626
      Unknown
      2019-11-11 13:57:00
      2019-11-11T13:57:00.000
      2019-09-11
      Cat
      Intact Female
      NaN
      Domestic Shorthair
      Brown Tabby
      Not adopted

      214
      A808466
      Unknown
      2019-11-10 09:35:00
      2019-11-10T09:35:00.000
      2019-09-09
      Cat
      Intact Male
      NaN
      Domestic Shorthair
      Brown/Black
      Not adopted

      347
      A808352
      Unknown
      2019-11-07 16:15:00
      2019-11-07T16:15:00.000
      2011-11-07
      Dog
      Intact Male
      NaN
      Dachshund Mix
      Tan
      Not adopted

      6649
      A752967
      Gray
      2019-07-24 11:42:00
      2019-07-24T11:42:00.000
      2015-06-29
      Dog
      NaN
      NaN
      Pit Bull Mix
      Blue/White
      Not adopted

```python
# 2 of the rows that are missing the age are also missing the sex so I will
# drop those:
adoption_upd = adoption_upd.dropna(subset = ['sex_upon_outcome'])
```

```python
# since there are no missing or unknown values for DOB which still seems strange
# especially for stray animals that were found but maybe they approximated. We can
# create a column where we subtract 2019 from the born on year to get the age and see
# if there are a lot of differences.

now = pd.Timestamp('now')
# first, get DOB year column and DOB month columns:
adoption_upd['DOB_month'] = adoption_upd['date_of_birth'].dt.month
# now, subtract current year from DOB year:
adoption_upd['DOB_year'] = now.year - adoption_upd['date_of_birth'].dt.year
# now turn DOB_year into months by multiplying by 12
adoption_upd['DOB_year'] = adoption_upd['DOB_year'] * 12
# add DOB_year which is now in months to DOB month and divide by 12 to get years
adoption_upd['calculated_age'] = adoption_upd['DOB_year'] + adoption_upd['DOB_month']
def calculate_age(age):
        if age >= 12:
            return(f'{age // 12} years')
        else:
            return(f'{age} months')
adoption_upd['calculated_age'] = adoption_upd['calculated_age'].apply(calculate_age)
# fill NaN's in age upon outcome column with calculated age
adoption_upd['age_upon_outcome'].fillna(adoption_upd['calculated_age'], inplace=True)
# drop DOB month, DOB year, calculated age:
adoption_upd.drop(columns =['DOB_month', 'DOB_year', 'calculated_age'], inplace=True)
```

```python
adoption_upd.isnull().sum()
# no more missing values!
```

    animal_id           0
    name                0
    datetime            0
    monthyear           0
    date_of_birth       0
    animal_type         0
    sex_upon_outcome    0
    age_upon_outcome    0
    breed               0
    color               0
    new_outcome_type    0
    dtype: int64

```python
adoption_upd.head()
```

      animal_id
      name
      datetime
      monthyear
      date_of_birth
      animal_type
      sex_upon_outcome
      age_upon_outcome
      breed
      color
      new_outcome_type

      1
      A781697
      Pookie
      2019-11-14 15:33:00
      2019-11-14T15:33:00.000
      2017-10-05
      Dog
      Spayed Female
      2 years
      Cairn Terrier
      White/Brown
      Adopted

      2
      A808382
      Unknown
      2019-11-14 14:57:00
      2019-11-14T14:57:00.000
      2014-11-08
      Cat
      Intact Male
      5 years
      Domestic Shorthair
      Orange Tabby
      Not adopted

      3
      A806701
      *Emerald
      2019-11-14 14:41:00
      2019-11-14T14:41:00.000
      2019-09-02
      Cat
      Neutered Male
      2 months
      Domestic Shorthair
      Blue Tabby/White
      Adopted

      4
      A804553
      *Wendy
      2019-11-14 14:36:00
      2019-11-14T14:36:00.000
      2019-08-09
      Cat
      Spayed Female
      3 months
      Domestic Shorthair
      Calico
      Adopted

      5
      A804552
      *Tinkerbell
      2019-11-14 14:35:00
      2019-11-14T14:35:00.000
      2019-08-09
      Cat
      Spayed Female
      3 months
      Domestic Shorthair
      Calico
      Adopted

```python
# looking at redundant columns, date of birth can be dropped since the ages are all accounted for.
# also datetime and monthyear are the same, I will get rid of datetime for now.
adoption_upd.drop(columns=['date_of_birth'], inplace=True)
```

```python
# going to create a new column for the season the animal came into the shelter to see if certain
# seasons have higher adoption rates:
adoption_upd['monthyear'] = pd.to_datetime(adoption_upd['monthyear'], infer_datetime_format=True)
adoption_upd['month_arrived'] = adoption_upd['monthyear'].dt.month
adoption_upd.head()

```

      animal_id
      name
      datetime
      monthyear
      animal_type
      sex_upon_outcome
      age_upon_outcome
      breed
      color
      new_outcome_type
      month_arrived

      1
      A781697
      Pookie
      2019-11-14 15:33:00
      2019-11-14 15:33:00
      Dog
      Spayed Female
      2 years
      Cairn Terrier
      White/Brown
      Adopted
      11

      2
      A808382
      Unknown
      2019-11-14 14:57:00
      2019-11-14 14:57:00
      Cat
      Intact Male
      5 years
      Domestic Shorthair
      Orange Tabby
      Not adopted
      11

      3
      A806701
      *Emerald
      2019-11-14 14:41:00
      2019-11-14 14:41:00
      Cat
      Neutered Male
      2 months
      Domestic Shorthair
      Blue Tabby/White
      Adopted
      11

      4
      A804553
      *Wendy
      2019-11-14 14:36:00
      2019-11-14 14:36:00
      Cat
      Spayed Female
      3 months
      Domestic Shorthair
      Calico
      Adopted
      11

      5
      A804552
      *Tinkerbell
      2019-11-14 14:35:00
      2019-11-14 14:35:00
      Cat
      Spayed Female
      3 months
      Domestic Shorthair
      Calico
      Adopted
      11

```python
def season(arrival_month):
    if arrival_month == 1 or arrival_month == 2 or arrival_month == 3:
        return 'Winter'
    if arrival_month == 3 or arrival_month == 4 or arrival_month ==5:
        return 'Spring'
    if arrival_month == 6 or arrival_month == 7 or arrival_month ==8:
        return 'Summer'
    if arrival_month == 9 or arrival_month == 10 or arrival_month ==11:
        return 'Fall'
adoption_upd['season_arrived'] = adoption_upd['month_arrived'].apply(season)
```

```python
# want to change the datetime column to year only since we have month_arrived now:
adoption_upd['year_arrived'] = adoption_upd['datetime'].dt.year
```

```python
# going to drop datetime and monthyear now:
adoption_upd.drop(columns=['datetime', 'monthyear'], inplace=True)
```

```python
adoption_upd.head(10)
```

      animal_id
      name
      animal_type
      sex_upon_outcome
      age_upon_outcome
      breed
      color
      new_outcome_type
      month_arrived
      season_arrived
      year_arrived

      1
      A781697
      Pookie
      Dog
      Spayed Female
      2 years
      Cairn Terrier
      White/Brown
      Adopted
      11
      Fall
      2019

      2
      A808382
      Unknown
      Cat
      Intact Male
      5 years
      Domestic Shorthair
      Orange Tabby
      Not adopted
      11
      Fall
      2019

      3
      A806701
      *Emerald
      Cat
      Neutered Male
      2 months
      Domestic Shorthair
      Blue Tabby/White
      Adopted
      11
      Fall
      2019

      4
      A804553
      *Wendy
      Cat
      Spayed Female
      3 months
      Domestic Shorthair
      Calico
      Adopted
      11
      Fall
      2019

      5
      A804552
      *Tinkerbell
      Cat
      Spayed Female
      3 months
      Domestic Shorthair
      Calico
      Adopted
      11
      Fall
      2019

      6
      A808132
      Daryl
      Cat
      Neutered Male
      5 years
      Domestic Shorthair
      Brown Tabby/White
      Adopted
      11
      Fall
      2019

      7
      A805655
      *Fezzik
      Cat
      Neutered Male
      2 months
      Domestic Shorthair
      Orange Tabby
      Adopted
      11
      Fall
      2019

      9
      A808526
      Jugez
      Dog
      Spayed Female
      13 years
      Shih Tzu
      Brown/Cream
      Not adopted
      11
      Fall
      2019

      10
      A787448
      Watermelon
      Dog
      Spayed Female
      3 years
      Labrador Retriever Mix
      Brown Tiger/White
      Adopted
      11
      Fall
      2019

      11
      A808522
      Unknown
      Cat
      Intact Female
      4 years
      Domestic Shorthair
      Brown Tabby
      Not adopted
      11
      Fall
      2019

```python
# how many different breeds are there:
adoption_upd['breed'].value_counts()
# 2042...very high cardinality
```

    Domestic Shorthair Mix                         26063
    Pit Bull Mix                                    4720
    Labrador Retriever Mix                          4372
    Chihuahua Shorthair Mix                         3974
    Domestic Shorthair                              3566
    Domestic Medium Hair Mix                        2548
    German Shepherd Mix                             1816
    Domestic Longhair Mix                           1184
    Siamese Mix                                     1019
    Australian Cattle Dog Mix                        969
    Dachshund Mix                                    625
    Border Collie Mix                                594
    Boxer Mix                                        572
    Domestic Medium Hair                             478
    Miniature Poodle Mix                             453
    Catahoula Mix                                    432
    Labrador Retriever                               403
    Staffordshire Mix                                401
    Chihuahua Shorthair                              401
    Pit Bull                                         384
    Australian Shepherd Mix                          381
    Great Pyrenees Mix                               367
    Pointer Mix                                      367
    Rat Terrier Mix                                  364
    Beagle Mix                                       358
    German Shepherd                                  348
    Yorkshire Terrier Mix                            346
    Siberian Husky Mix                               344
    Jack Russell Terrier Mix                         338
    Cairn Terrier Mix                                335
                                                   ...
    West Highland/Lhasa Apso                           1
    Vizsla/Greyhound                                   1
    Pembroke Welsh Corgi/Collie Smooth                 1
    Whippet/Catahoula                                  1
    Yorkshire Terrier/Dachshund Longhair               1
    Staffordshire/Border Collie                        1
    French Bulldog/Chihuahua Shorthair                 1
    Dachshund Longhair/Cairn Terrier                   1
    Standard Poodle/Whippet                            1
    Bluetick Hound/Great Pyrenees                      1
    Labrador Retriever/Collie Rough                    1
    Pbgv/Pit Bull                                      1
    English Bulldog/Boxer                              1
    Chinese Sharpei/Chow Chow                          1
    Domestic Shorthair/Manx                            1
    Tibetan Spaniel/Dachshund Longhair                 1
    Tibetan Spaniel                                    1
    Collie Rough/Chow Chow                             1
    Vizsla/Dachshund                                   1
    Snowshoe/Ragdoll                                   1
    Jack Russell Terrier/Australian Shepherd           1
    Golden Retriever/Akita                             1
    Chinese Crested/Papillon                           1
    Labrador Retriever/English Springer Spaniel        1
    English Bulldog/Mastiff                            1
    Rhod Ridgeback/Pointer                             1
    Cocker Spaniel/Dachshund Longhair                  1
    Akita/Pit Bull                                     1
    Chihuahua Shorthair/Cavalier Span                  1
    Black Mouth Cur/Belgian Malinois                   1
    Name: breed, Length: 1867, dtype: int64

```python
# create subsets for dogs and cats:
dogs = adoption_upd[adoption_upd.animal_type=='Dog']
cats = adoption_upd[adoption_upd.animal_type=='Cat']
```

```python
# Reduce cardinality for breed feature ...
dogs =dogs.copy()
# Get a list of the top 75 breeds
top75 = dogs['breed'].value_counts()[:75].index
# Breeds that are NOT in the top 100,
# replace the breed with 'OTHER'
dogs.loc[~dogs['breed'].isin(top75), 'breed'] = 'OTHER'
dogs['breed'].value_counts()
```

    OTHER                                       9368
    Pit Bull Mix                                4720
    Labrador Retriever Mix                      4372
    Chihuahua Shorthair Mix                     3974
    German Shepherd Mix                         1816
    Australian Cattle Dog Mix                    969
    Dachshund Mix                                625
    Border Collie Mix                            594
    Boxer Mix                                    572
    Miniature Poodle Mix                         453
    Catahoula Mix                                432
    Labrador Retriever                           403
    Chihuahua Shorthair                          401
    Staffordshire Mix                            401
    Pit Bull                                     384
    Australian Shepherd Mix                      381
    Great Pyrenees Mix                           367
    Pointer Mix                                  367
    Rat Terrier Mix                              364
    Beagle Mix                                   358
    German Shepherd                              348
    Yorkshire Terrier Mix                        346
    Siberian Husky Mix                           344
    Jack Russell Terrier Mix                     338
    Cairn Terrier Mix                            335
    Chihuahua Longhair Mix                       329
    Miniature Schnauzer Mix                      315
    Plott Hound Mix                              300
    Anatol Shepherd Mix                          298
    Black Mouth Cur Mix                          267
                                                ...
    Golden Retriever Mix                         122
    Queensland Heeler Mix                        120
    Maltese Mix                                  120
    Dachshund                                     97
    Shih Tzu                                      95
    Black/Tan Hound Mix                           90
    Labrador Retriever/Border Collie              88
    Manchester Terrier Mix                        88
    Cardigan Welsh Corgi Mix                      88
    Lhasa Apso Mix                                88
    Basset Hound Mix                              87
    Doberman Pinsch Mix                           87
    Boxer                                         83
    Chow Chow Mix                                 83
    Pit Bull/Labrador Retriever                   83
    Collie Smooth Mix                             79
    Labrador Retriever/Great Pyrenees             79
    Dachshund Longhair Mix                        78
    Cocker Spaniel Mix                            78
    Rottweiler                                    76
    Dachshund Wirehair Mix                        72
    Labrador Retriever/Australian Cattle Dog      69
    Pug Mix                                       69
    Mastiff Mix                                   68
    Pomeranian Mix                                68
    Great Pyrenees                                67
    Cairn Terrier                                 66
    Border Collie/Labrador Retriever              66
    Flat Coat Retriever Mix                       66
    Chinese Sharpei Mix                           66
    Name: breed, Length: 76, dtype: int64

```python
# Reduce cardinality for breed feature ...cats don't have as many
# breeds so will only use top 30
cats =cats.copy()
# Get a list of the top 30 breeds
top30 = cats['breed'].value_counts()[:30].index
# Breeds that are NOT in the top 30,
# replace the breed with 'OTHER'
cats.loc[~cats['breed'].isin(top30), 'breed'] = 'OTHER'
cats['breed'].value_counts()
```

    Domestic Shorthair Mix         26063
    Domestic Shorthair              3566
    Domestic Medium Hair Mix        2548
    Domestic Longhair Mix           1184
    Siamese Mix                     1019
    Domestic Medium Hair             478
    American Shorthair Mix           201
    Snowshoe Mix                     143
    Siamese                          130
    OTHER                            125
    Domestic Longhair                103
    Maine Coon Mix                    81
    Manx Mix                          75
    Russian Blue Mix                  46
    Ragdoll Mix                       34
    American Shorthair                29
    Himalayan Mix                     27
    Persian Mix                       16
    Balinese Mix                      14
    American Curl Shorthair Mix       12
    Japanese Bobtail Mix               9
    Persian                            9
    Russian Blue                       8
    Tonkinese Mix                      8
    Turkish Van Mix                    7
    Siamese/Domestic Shorthair         7
    Manx                               7
    Himalayan                          7
    Cymric Mix                         6
    Abyssinian                         6
    Snowshoe                           6
    Name: breed, dtype: int64

```python
# concatenate the cats and dogs subsets, matches shape of adoption_upd
adoption_final = pd.concat([dogs, cats])
adoption_final.shape
```

    (75730, 11)

```python
# lets do something similar to color:
adoption_final['color'].value_counts()
```

    Black/White                  8297
    Black                        6815
    Brown Tabby                  5511
    Brown Tabby/White            2809
    Orange Tabby                 2642
    White                        2320
    Tan/White                    2258
    Brown/White                  2151
    Blue/White                   2113
    White/Black                  2067
    Tan                          1869
    Tortie                       1664
    Calico                       1636
    Brown                        1620
    Tricolor                     1612
    Blue                         1540
    Black/Tan                    1514
    Blue Tabby                   1381
    Black/Brown                  1377
    White/Brown                  1302
    Orange Tabby/White           1289
    Brown Brindle/White          1196
    White/Tan                    1106
    Torbie                       1035
    Brown/Black                  1025
    Red                           748
    Red/White                     704
    Blue Tabby/White              675
    Tan/Black                     675
    Brown Brindle                 657
                                 ...
    Brown/Liver                     1
    Gray/Blue Merle                 1
    Tricolor/Orange                 1
    Blue Smoke/Gray                 1
    Tricolor/Red Tick               1
    White/Liver Tick                1
    Red Tick/Tricolor               1
    Black Tabby/Black               1
    Red/Brown Brindle               1
    Calico/Orange Tabby             1
    Brown Brindle/Blue              1
    Lynx Point/Blue                 1
    Gold/Yellow                     1
    Gray Tabby/Orange               1
    Lilac Point/Gray                1
    Red/Silver                      1
    White/Yellow Brindle            1
    Blue/Calico                     1
    Tortie/Tortie                   1
    Red Merle/Black                 1
    Buff/Yellow                     1
    Black Smoke/Black Tiger         1
    Brown Tabby/Black Brindle       1
    Fawn/Chocolate                  1
    Brown Merle/Chocolate           1
    Cream Tabby/Cream Tabby         1
    Orange/Orange Tabby             1
    Red/Red Merle                   1
    Orange Tabby/Tortie Point       1
    Gray/Buff                       1
    Name: color, Length: 477, dtype: int64

```python
adoption_final = adoption_final.copy()
# Get a list of the top 75 colors
top75 = adoption_final['color'].value_counts()[:75].index
# colors that are NOT in the top 75,
# replace the color with 'OTHER'
adoption_final.loc[~adoption_final['color'].isin(top75), 'color'] = 'OTHER'
```

```python
adoption_final.head()
```

      animal_id
      name
      animal_type
      sex_upon_outcome
      age_upon_outcome
      breed
      color
      new_outcome_type
      month_arrived
      season_arrived
      year_arrived

      1
      A781697
      Pookie
      Dog
      Spayed Female
      2 years
      Cairn Terrier
      White/Brown
      Adopted
      11
      Fall
      2019

      9
      A808526
      Jugez
      Dog
      Spayed Female
      13 years
      Shih Tzu
      OTHER
      Not adopted
      11
      Fall
      2019

      10
      A787448
      Watermelon
      Dog
      Spayed Female
      3 years
      Labrador Retriever Mix
      OTHER
      Adopted
      11
      Fall
      2019

      17
      A808589
      Lola
      Dog
      Spayed Female
      5 years
      OTHER
      Brown Brindle
      Not adopted
      11
      Fall
      2019

      18
      A808590
      Cheyenne
      Dog
      Spayed Female
      7 years
      Australian Shepherd Mix
      Brown/White
      Not adopted
      11
      Fall
      2019

```python
# # change animal type to numerical:
# # 1 for dog, 2 for cat
# def change_type(animal):
#     if animal == 'dog':
#         return 1
#     else:
#         return 2

```

```python
# adoption_final['animal_type'] = adoption_final['animal_type'].apply(change_type).astype(int)
```

```python
adoption_final['sex_upon_outcome'].value_counts()
```

    Neutered Male    27406
    Spayed Female    25892
    Intact Female     9822
    Intact Male       8973
    Unknown           3637
    Name: sex_upon_outcome, dtype: int64

```python
# do the same thing with sex_upon_outcome:
def change_sex(animal):
    if animal == 'Neutered Male':
        return 1
    if animal == 'Spayed Female':
        return 2
    if animal == 'Intact Male':
        return 3
    if animal == 'Intact Female':
        return 4
    if animal == 'Unknown':
        return 5
adoption_final['sex_upon_outcome'] = adoption_final['sex_upon_outcome'].apply(change_sex).astype(int)
```

```python
#need to bin ages:
adoption_final['age_upon_outcome'].value_counts()
```

    1 year       12232
    2 months     11998
    2 years       9739
    3 months      4480
    1 month       4321
    3 years       3684
    4 months      2890
    4 years       2126
    5 years       2050
    5 months      1979
    6 months      1818
    3 weeks       1756
    2 weeks       1664
    8 months      1321
    6 years       1302
    8 years       1191
    4 weeks       1180
    10 months     1158
    7 years       1064
    7 months      1000
    10 years       955
    9 months       794
    1 weeks        656
    9 years        582
    1 week         547
    12 years       466
    11 months      461
    11 years       315
    2 days         270
    3 days         262
    13 years       251
    1 day          184
    6 days         182
    4 days         157
    14 years       156
    15 years       137
    5 days         108
    0 years        103
    5 weeks         92
    16 years        39
    17 years        28
    18 years        15
    19 years         8
    20 years         7
    -1 years         1
    22 years         1
    Name: age_upon_outcome, dtype: int64

```python
# This is difficult because of the age units, there are days, weeks, months
# and years. Best way I could think of it is to bin the ages that are under 1 year
# and then do the rest of the data in years since that's the majority and it's numerical data.

def bin_ages(age):
    if age == '11 months' or age == '12 months' or age == '1 year':
        return '1 years'
    if age == '8 months' or age == '9 months' or age == '10 months':
        return '.75 years'
    if age == '6 months' or age == '7 months' or age =='5 months' or age == '4 months':
        return '.5 years'
    if age == '1 month' or age == '3 weeks' or age == '2 weeks' or age == '4 weeks' or age == '1 weeks' or age == '1 week' or age == '2 days' or age == '3 days' or age == '1 day' or age == '6 days' or age == '4 days' or age == '5 days' or age == '0 years' or age == '5 weeks' or age == '2 months' or age == '3 months' or age == '2 month' or age == '-1 years':
        return '.25 years'
    else:
        return age
adoption_final['age_upon_outcome'] = adoption_final['age_upon_outcome'].apply(bin_ages)
```

```python
adoption_final['age_upon_outcome'].value_counts()
```

    .25 years    27961
    1 years      12693
    2 years       9739
    .5 years      7687
    3 years       3684
    .75 years     3273
    4 years       2126
    5 years       2050
    6 years       1302
    8 years       1191
    7 years       1064
    10 years       955
    9 years        582
    12 years       466
    11 years       315
    13 years       251
    14 years       156
    15 years       137
    16 years        39
    17 years        28
    18 years        15
    19 years         8
    20 years         7
    22 years         1
    Name: age_upon_outcome, dtype: int64

```python
# now for the rest of the ages, need to strip the 'years' from the values:
# also going to drop the row that says -1 because not sure what that means.
def age_to_int(age):
  return float(age.strip('years'))

adoption_final['age_upon_outcome'] = adoption_final['age_upon_outcome'].apply(age_to_int)
```

```python
adoption_final.dtypes
```

    animal_id            object
    name                 object
    animal_type          object
    sex_upon_outcome      int64
    age_upon_outcome    float64
    breed                object
    color                object
    new_outcome_type     object
    month_arrived         int64
    season_arrived       object
    year_arrived          int64
    dtype: object

```python
adoption_final.head()
```

      animal_id
      name
      animal_type
      sex_upon_outcome
      age_upon_outcome
      breed
      color
      new_outcome_type
      month_arrived
      season_arrived
      year_arrived

      1
      A781697
      Pookie
      Dog
      2
      2.0
      Cairn Terrier
      White/Brown
      Adopted
      11
      Fall
      2019

      9
      A808526
      Jugez
      Dog
      2
      13.0
      Shih Tzu
      OTHER
      Not adopted
      11
      Fall
      2019

      10
      A787448
      Watermelon
      Dog
      2
      3.0
      Labrador Retriever Mix
      OTHER
      Adopted
      11
      Fall
      2019

      17
      A808589
      Lola
      Dog
      2
      5.0
      OTHER
      Brown Brindle
      Not adopted
      11
      Fall
      2019

      18
      A808590
      Cheyenne
      Dog
      2
      7.0
      Australian Shepherd Mix
      Brown/White
      Not adopted
      11
      Fall
      2019

```python
# going to make a copy without the age upon outcome column since it has high cardinality and isn't numeric:
adoption_use = adoption_final.drop(columns=['age_upon_outcome'])
```

```python
adoption_use.head()
```

      animal_id
      name
      animal_type
      sex_upon_outcome
      breed
      color
      new_outcome_type
      month_arrived
      season_arrived
      year_arrived

      1
      A781697
      Pookie
      Dog
      2
      Cairn Terrier
      White/Brown
      Adopted
      11
      Fall
      2019

      9
      A808526
      Jugez
      Dog
      2
      Shih Tzu
      OTHER
      Not adopted
      11
      Fall
      2019

      10
      A787448
      Watermelon
      Dog
      2
      Labrador Retriever Mix
      OTHER
      Adopted
      11
      Fall
      2019

      17
      A808589
      Lola
      Dog
      2
      OTHER
      Brown Brindle
      Not adopted
      11
      Fall
      2019

      18
      A808590
      Cheyenne
      Dog
      2
      Australian Shepherd Mix
      Brown/White
      Not adopted
      11
      Fall
      2019

Think I'm ready to start fitting models, may need to come back to features:

```python
# split data into train, val, test
adoption_use['year_arrived'].value_counts()
```

    2015    13964
    2019    13644
    2016    13141
    2017    13035
    2018    12513
    2014     9433
    Name: year_arrived, dtype: int64

```python
test = adoption_use[(adoption_use['year_arrived'] == 2019)]
val =  adoption_use[(adoption_use['year_arrived']== 2018)]
train = adoption_upd[(adoption_upd['year_arrived'] < 2018)]
```

```python
test.shape, val.shape, train.shape
```

    ((13644, 10), (12513, 10), (49573, 11))

```python
# %matplotlib inline
# import matplotlib.pyplot as plt
# import seaborn as sns

# for col in adoption_upd.columns:
#     if adoption_upd[col].nunique() < 10:
#         try:
#             sns.catplot(x=col, y='new_outcome_type', data=adoption_upd, kind='bar', color='grey')
#             plt.show()
#         except:
#             pass
```

```python
# numeric = train.select_dtypes('number')
# def change_type(animal):
#     if animal == 'Adopted':
#         return 1
#     else:
#         return 2
# train['new_outcome_type'] = train['new_outcome_type'].apply(change_type)
# for col in sorted(numeric.columns):
#     sns.lmplot(x=col, y='new_outcome_type', data=train, scatter_kws=dict(alpha=0.05))
#     plt.show()
```

```python
train.dtypes
```

    animal_id           object
    name                object
    animal_type         object
    sex_upon_outcome    object
    age_upon_outcome    object
    breed               object
    color               object
    new_outcome_type    object
    month_arrived        int64
    season_arrived      object
    year_arrived         int64
    dtype: object

```python

```


```python
#format the book
%matplotlib inline
from __future__ import division, print_function
import sys;sys.path.insert(0,'..')
from  book_format import load_style;load_style('..')
```

<style>
@import url('http://fonts.googleapis.com/css?family=Source+Code+Pro');
@import url('http://fonts.googleapis.com/css?family=Vollkorn');
@import url('http://fonts.googleapis.com/css?family=Arimo');
@import url('http://fonts.googleapis.com/css?family=Fira+sans');

.CodeMirror pre {
    font-family: 'Source Code Pro', Consolas, monocco, monospace;
}
    div.cell{
        width: 900px;
        margin-left: 0% !important;
        margin-right: auto;
    }
    div.text_cell code {
        background: transparent;
        color: #000000;
        font-weight: 600;
        font-size: 11pt;
        font-style: bold;
        font-family:  'Source Code Pro', Consolas, monocco, monospace;
   }
    h1 {
        font-family: 'Open sans',verdana,arial,sans-serif;
	}

    div.input_area {
        background: #F6F6F9;
        border: 1px solid #586e75;
    }

    .text_cell_render h1 {
        font-weight: 200;
        font-size: 30pt;
        line-height: 100%;
        color:#c76c0c;
        margin-bottom: 0.5em;
        margin-top: 1em;
        display: block;
        white-space: wrap;
        text-align: left;
    }
    h2 {
        font-family: 'Open sans',verdana,arial,sans-serif;
        text-align: left;
    }
    .text_cell_render h2 {
        font-weight: 200;
        font-size: 16pt;
        font-style: italic;
        line-height: 100%;
        color:#c76c0c;
        margin-bottom: 0.5em;
        margin-top: 1.5em;
        display: block;
        white-space: wrap;
        text-align: left;
    }
    h3 {
        font-family: 'Open sans',verdana,arial,sans-serif;
    }
    .text_cell_render h3 {
        font-weight: 200;
        font-size: 14pt;
        line-height: 100%;
        color:#d77c0c;
        margin-bottom: 0.5em;
        margin-top: 2em;
        display: block;
        white-space: wrap;
        text-align: left;
    }
    h4 {
        font-family: 'Open sans',verdana,arial,sans-serif;
    }
    .text_cell_render h4 {
        font-weight: 100;
        font-size: 14pt;
        color:#d77c0c;
        margin-bottom: 0.5em;
        margin-top: 0.5em;
        display: block;
        white-space: nowrap;
    }
    h5 {
        font-family: 'Open sans',verdana,arial,sans-serif;
    }

    .text_cell_render h5 {
        font-weight: 200;
        font-style: normal;
        color: #1d3b84;
        font-size: 16pt;
        margin-bottom: 0em;
        margin-top: 0.5em;
        display: block;
        white-space: nowrap;
    }
    div.text_cell_render{
        font-family: 'Vollkorn', verdana,arial,sans-serif;
        line-height: 150%;
        font-size: 130%;
        font-weight: 400;
        text-align:justify;
        text-justify:inter-word;
    }
    div.output_subarea.output_text.output_pyout {
        overflow-x: auto;
        overflow-y: scroll;
        max-height: 50000px;
    }
    div.output_subarea.output_stream.output_stdout.output_text {
        overflow-x: auto;
        overflow-y: scroll;
        max-height: 50000px;
    }
    div.output_wrapper{
        margin-top:0.2em;
        margin-bottom:0.2em;
}

    code{
        font-size: 6pt;

    }
    .rendered_html code{
    background-color: transparent;
    }
    ul{
        margin: 2em;
    }
    ul li{
        padding-left: 0.5em;
        margin-bottom: 0.5em;
        margin-top: 0.5em;
    }
    ul li li{
        padding-left: 0.2em;
        margin-bottom: 0.2em;
        margin-top: 0.2em;
    }
    ol{
        margin: 2em;
    }
    ol li{
        padding-left: 0.5em;
        margin-bottom: 0.5em;
        margin-top: 0.5em;
    }
    ul li{
        padding-left: 0.5em;
        margin-bottom: 0.5em;
        margin-top: 0.2em;
    }
    a:link{
       font-weight: bold;
       color:#447adb;
    }
    a:visited{
       font-weight: bold;
       color: #1d3b84;
    }
    a:hover{
       font-weight: bold;
       color: #1d3b84;
    }
    a:focus{
       font-weight: bold;
       color:#447adb;
    }
    a:active{
       font-weight: bold;
       color:#447adb;
    }
    .rendered_html :link {
       text-decoration: underline;
    }
    .rendered_html :hover {
       text-decoration: none;
    }
    .rendered_html :visited {
      text-decoration: none;
    }
    .rendered_html :focus {
      text-decoration: none;
    }
    .rendered_html :active {
      text-decoration: none;
    }
    .warning{
        color: rgb( 240, 20, 20 )
    }
    hr {
      color: #f3f3f3;
      background-color: #f3f3f3;
      height: 1px;
    }
    blockquote{
      display:block;
      background: #fcfcfc;
      border-left: 5px solid #c76c0c;
      font-family: 'Open sans',verdana,arial,sans-serif;
      width:680px;
      padding: 10px 10px 10px 10px;
      text-align:justify;
      text-justify:inter-word;
      }
      blockquote p {
        margin-bottom: 0;
        line-height: 125%;
        font-size: 100%;
      }
</style>

# Computing and plotting PDFs of discrete data

So let's investigate how to compute and plot probability distributions.

First, let's make some data according to a normal distribution. We use `numpy.random.normal` for this. The parameters are not well named. `loc` is the mean of the distribution, and `scale` is the standard deviation. We can call this function to create an arbitrary number of data points that are distributed according to that mean and std.

```python
import numpy as np
import numpy.random as random

mean = 3
std = 2

data = random.normal(loc=mean, scale=std, size=50000)
print(len(data))
print(data.mean())
print(data.std())
```

    50000
    2.99685400941
    1.99912142836

As you can see from the print statements we got 5000 points that have a mean very close to 3, and a standard deviation close to 2.

We can plot this Gaussian by using `scipy.stats.norm` to create a frozen function that we will then use to compute the pdf (probability distribution function) of the Gaussian.

```python
%matplotlib inline
import matplotlib.pyplot as plt
import scipy.stats as stats

def plot_normal(xs, mean, std, **kwargs):
    norm = stats.norm(mean, std)
    plt.plot(xs, norm.pdf(xs), **kwargs)

xs = np.linspace(-5, 15, num=200)
plot_normal(xs, mean, std, color='k')
```

But we really want to plot the PDF of the discrete data, not the idealized function.

There are a couple of ways of doing that. First, we can take advantage of `matplotlib`'s `hist` method, which computes a histogram of a collection of data. Normally `hist` computes the number of points that fall in a bin, like so:

```python
plt.hist(data, bins=200)
plt.show()
```

that is not very useful to us - we want the PDF, not bin counts. Fortunately `hist` includes a `density` parameter which will plot the PDF for us.

```python
plt.hist(data, bins=200, normed=True)
plt.show()
```

I may not want bars, so I can specify the `histtype` as 'step' to get a line.

```python
plt.hist(data, bins=200, normed=True, histtype='step', lw=2)
plt.show()
```

To be sure it is working, let's also plot the idealized Gaussian in black.

```python
plt.hist(data, bins=200, normed=True, histtype='step', lw=2)
norm = stats.norm(mean, std)
plt.plot(xs, norm.pdf(xs), color='k', lw=2)
plt.show()
```

There is another way to get the approximate distribution of a set of data. There is a technique called *kernel density estimate* that uses a kernel to estimate the probability distribution of a set of data. NumPy implements it with the function `gaussian_kde`. Do not be mislead by the name - Gaussian refers to the type of kernel used in the computation. This works for any distribution, not just Gaussians. In this section we have a Gaussian distribution, but soon we will not, and this same function will work.

```python
kde = stats.gaussian_kde(data)

xs = np.linspace(-5, 15, num=200)
plt.plot(xs, kde(xs))
plt.show()
```

## Monte Carlo Simulations

We (well I) want to do this sort of thing because I want to use monte carlo simulations to compute distributions. It is easy to compute Gaussians when they pass through linear functions, but difficult to impossible to compute them analytically when passed through nonlinear functions. Techniques like particle filtering handle this by taking a large sample of points, passing them through a nonlinear function, and then computing statistics on the transformed points. Let's do that.

We will start with the linear function $f(x) = 2x + 12$ just to prove to ourselves that the code is working. I will alter the mean and std of the data we are working with to help ensure the numbers that are output are unique It is easy to be fooled, for example, if the formula multipies x by 2, the mean is 2, and the std is 2. If the output of something is 4, is that due to the multication factor, the mean, the std, or a bug? It's hard to tell.

```python
def f(x):
    return 2*x + 12

mean = 1.
std = 1.4
data = random.normal(loc=mean, scale=std, size=50000)

d_t = f(data) # transform data through f(x)

plt.hist(data, bins=200, normed=True, histtype='step', lw=2)
plt.hist(d_t, bins=200, normed=True, histtype='step', lw=2)

plt.ylim(0, .35)
plt.show()
print('mean = {:.2f}'.format(d_t.mean()))
print('std  = {:.2f}'.format(d_t.std()))
```

This is what we expected. The input is the Gaussian $\mathcal{N}(\mu=1, \sigma=1.4)$, and the function is $f(x) = 2x+1$. Therefore we expect the mean to be shifted to $f(\mu) = 2*1+12=14$. We can see from the plot and the print statement that this is what happened.

Before I go on, can you explain what happened to the standard deviation? You may have thought that the new $\sigma$ should be passed through $f(x)$ like so $2(1.4) + 12=14.81$. But that is not correct - the standard deviation is only affected by the multiplicative factor, not the shift. If you think about that for a moment you will see it makes sense. We multiply our samples by 2, so they are twice as spread out as before. Standard deviation is a measure of how spread out things are, so it should also double. It doesn't matter if we then shift that distribution 12 places, or 12 million for that matter - the spread is still twice the input data.

## Nonlinear Functions

Now that we believe in our code, lets try it with nonlinear functions.

```python
def f2(x):
    return (np.cos((1.5*x + 2.1))) * np.sin(0.3*x) - 1.6*x

d_t = f2(data)
plt.subplot(121)
plt.hist(d_t, bins=200, normed=True, histtype='step', lw=2)

plt.subplot(122)
kde = stats.gaussian_kde(d_t)
xs = np.linspace(-10, 10, 200)
plt.plot(xs, kde(xs), 'k')
plot_normal(xs, d_t.mean(), d_t.std(), color='g', lw=3)
plt.show()
print('mean = {:.2f}'.format(d_t.mean()))
print('std  = {:.2f}'.format(d_t.std()))
```

Here I passed the data through the nonlinear function $f(x) = \cos(1.5x+2.1)\sin(\frac{x}{3}) - 1.6x$. That function is quite close to linear, but we can see how much it alters the pdf of the sampled data.

There is a lot of computation going on behind the scenes to transform 50,000 points and then compute their PDF. The Extended Kalman Filter (EKF) gets around this by linearizing the function at the mean and then passing the Gaussian through the linear equation. We saw above how easy it is to pass a Gaussian through a linear function. So lets try that.

We can linearize this by taking the derivative of the function at x. We can use sympy to get the derivative.

```python
import sympy
x = sympy.symbols('x')
f = sympy.cos(1.5*x+2.1) * sympy.sin(x/3) - 1.6*x
dfx = sympy.diff(f, x)
dfx
```

    -1.5*sin(x/3)*sin(1.5*x + 2.1) + cos(x/3)*cos(1.5*x + 2.1)/3 - 1.6

We can now compute the slope of the function by evaluating the derivative at the mean.

```python
m = dfx.subs(x, mean)
m
```

    -1.66528051815545

The equation of a line is $y=mx+b$, so the new standard deviation should be $~1.67$ times the input std. We can compute the new mean by passing it through the original function because the linearized function is just the slope of f(x) evaluated at the mean. The slope is a tangent that touches the function at $x$, so both will return the same result. So, let's plot this and compare it to the results from the monte carlo simulation.

```python
plt.hist(d_t, bins=200, normed=True, histtype='step', lw=2)
plot_normal(xs, f2(mean), abs(float(m)*std), color='k', lw=3, label='EKF')
plot_normal(xs, d_t.mean(), d_t.std(), color='r', lw=3, label='MC')
plt.legend()
plt.show()
```

We can see from this that the estimate from the EKF (in red) is not exact, but it is not a bad approximation either.


**Submitted by**:

[Ray], [Zhu], [LinkedIn](https://www.linkedin.com/in/jiasheng-ray-zhu-845241177/),
Majoring in [Political Economy with Economics Concentration]
Class of [2022]
Duke Kunshan University

---

*Disclaimer: Submissions to Problem Set 1 for COMPSCI/ECON 206 Computational Microeconomics, 2022 Spring Term (Seven Week - Second) instructed by [Prof. Luyao Zhang](http://scholars.duke.edu/person/luyao.zhang) at Duke Kunshan University.*

**Resources:**

*   Colab Instructions: https://colab.research.google.com/
*   Markdown Guide: https://www.markdownguide.org
*   Latex Math wiki: https://en.wikibooks.org/wiki/LaTeX/Mathematics

*   OER: https://ie.pubpub.org/pub/gpd9nks0/release/2

---

# Part I: Game Theory Concepts (6 points)

**Note:**

*if you can't find definitions in the second computer science textbook, you can also refer to the algorithmic game theory textbook for substitutions.*

## 1. Normal Form Game

### 1.1. What defines a normal form game? Find answers in “A Course in Game Theory by Prof. Ariel Rubinstein and M. Osborne. ([Download](https://arielrubinstein.tau.ac.il/books.html))

Refer to Textbook: [Osborne, Martin J. and Ariel Rubinstein](https://arielrubinstein.tau.ac.il/books.html). 1994. A Course in Game Theory. (Chapter 1, Page 9)

In this part we study a model of strategic interaction known as a strategic game, or, in the terminology of von Neumann and Morgenstern (1944), a “game in normal form”. This model specifies for each player a set of possible actions and a preference ordering over the set of possible action profiles.

### 1.2. What defines a normal form game? Find answers in “Multiagent Systems: Algorithmic, Game-Theoretic, and Logical Foundations” by Prof. Yoav Shoham and Kevin Leyton-Brown. ([Download](http://www.masfoundations.org/download.html))

Refer to Textbook: [Noam and Nisan.](https://www.cs.cmu.edu/~sandholm/cs15-892F13/algorithmic-game-theory.pdf) 2007. Algorithmic Game Theory (Page 182)

A multiplayer game consists of $n$ players, each with a finite set of pure strategies or actions available to them, along with a specification of the payoffs to each player. Throughout the chapter, we use $a_{i}$ to denote the action chosen by player $i$. For simplicity we will assume a binary action space, so $a_{i} \in\{0,1\}$. (The generalization of the results examined here to the multiaction setting is straightforward.) The payoffs to player $i$ are given by a table or matrix $M_{i}$, indexed by the joint action $\vec{a} \in\{0,1\}^{n}$. The value $M_{i}(\vec{a})$, which we assume without loss of generality to lie in the interval $[0,1]$, is the payoff to player $i$ resulting from the joint action $\vec{a}$. Multiplayer games described in this way are referred to as normal form games.

## 2. Static Game with Complete Information and Nash Equilibrium

### 2.1. What defines Nash Equilibrium? Find answers in “A Course in Game Theory by Prof. Ariel Rubinstein and M. Osborne. ([Download](https://arielrubinstein.tau.ac.il/books.html))

Refer to Textbook: [Osborne, Martin J. and Ariel Rubinstein](https://arielrubinstein.tau.ac.il/books.html). 1994. A Course in Game Theory. (Chapter 2, Page 14, DEFINITION 14.1)

**Definition of Nash equilibrium**

A Nash Equilibrium of a strategic game $\langle{N}, {A_{i}},(\succeq_{i})\rangle$ is a profile $a^{*}\in{A}$ of actions with the property that for every player $i\in {N}$, we have:
$$(a^{*}_{-i},a^{*}_{i}) \succeq_{i}(a^{*}_{-i},a_{i}), \forall \in {A_{i}}.$$
And, a strategic game $\langle{N}, {A_{i}},(\succeq_{i})\rangle$  consist of:
*   a finite set ${N}$ as the set of players
*   for each player $i\in{N}$, a nonempty set $A_{i}$ as the set of actions available to player $i$
*   for each player $i\in{N}$, a preference relation $\succeq_{i}$ on ${A}=\times_{j\in {N}}{A}_{j}$

### 2.2. What defines Nash Equilibrium? Find answers in “Multiagent Systems: Algorithmic, Game-Theoretic, and Logical Foundations” by Prof. Yoav Shoham and Kevin Leyton-Brown. ([Download](http://www.masfoundations.org/download.html))

Refer to Textbook: [Shoham, Yoav, and Kevin Leyton-Brown](http://www.masfoundations.org/download.html). 2008. Multiagent Systems: Algorithmic, Game-Theoretic, and Logical Foundations. Cambridge: Cambridge University Press. (Chapter 3, Page 62, Definition 3.3.4)

**Definition of Nash equilibrium**

Nash Equilibrium A strategy profile $s^{*}=(s_{1}^{*},...,s_{n}^{*})\in S$ is a \textbf{Nash Equilibrium} of a normal for game $({N}, {A}, \mu)$ if, $\forall$ agents $i$, $s_{i}^{*}$ is a best response to $s_{-i}^{*}$:

$$\mu_{i}(s_{i}^{*},s_{-i}^{*}) \geq \mu_{i}(s_{i},s_{-i}^{*}), \forall -i.$$
And a normal game $({N}, {A}, \mu)$ consist of:
*   ${N}$, a finite set of $n$ players, indexed by $i$
*   ${A} ={A_{1}}\times ...{A_{n}}$, where ${A_{i}}$ is a finite set of actions available to player $i$. Each vector $a=(a_{1},...,a_{n})\in A$ is called an action profile; the set of mixed strategy for player $i$ is $S_{i}=\prod(A_{i})$, where for any set $X$, $\prod(X)$ denotes the set of all probability distributions over $X$
*   $\mu = (\mu_{1},...,\mu_{n})$ where $\mu_{i}: {A} \mapsto \mathbb{R} $

## 3.Bayesian Game and Perfect Bayesian Nash Equilibrium

### 3.1. How to define Bayesian Game and Bayesian Nash Equilibrium? Find answers in “A Course in Game Theory by Prof. Ariel Rubinstein and M. Osborne. ([Download](https://arielrubinstein.tau.ac.il/books.html))

Refer to Textbook: [Osborne, Martin J. and Ariel Rubinstein](https://arielrubinstein.tau.ac.il/books.html). 1994. A Course in Game Theory. (Chapter 2, Page 25-26, DEFINITION 14.1)

DEFINITION 25.1 A Bayesian game consists of
- a finite set $N$ (the set of players)
- a finite set $\Omega$ (the set of states)
and for each player $i \in N$
- a set $A_{i}$ (the set of actions available to player $i$ )
- a finite set $T_{i}$ (the set of signals that may be observed by player $i$ ) and a function $\tau_{i}: \Omega \rightarrow T_{i}$ (the signal function of player $i$ )
- a probability measure $p_{i}$ on $\Omega$ (the prior belief of player $i$ ) for which $p_{i}\left(\tau_{i}^{-1}\left(t_{i}\right)\right)>0$ for all $t_{i} \in T_{i}$
- a preference relation $\succsim_{i}$ on the set of probability measures over $A \times \Omega$ (the preference relation of player $i$ ), where $A=\times_{j \in N} A_{j}$.

Definition $26.1$ A Nash equilibrium of a Bayesian game $\langle N$, $\left.\Omega,\left(A_{i}\right),\left(T_{i}\right),\left(\tau_{i}\right),\left(p_{i}\right),\left(\succsim_{i}\right)\right\rangle$ is a Nash equilibrium of the strategic game defined as follows.
- The set of players is the set of all pairs $\left(i, t_{i}\right)$ for $i \in N$ and $t_{i} \in T_{i}$.
- The set of actions of each player $\left(i, t_{i}\right)$ is $A_{i}$.
- The preference ordering $\succsim_{\left(i, t_{i}\right)}^{*}$ of each player $\left(i, t_{i}\right)$ is defined by $a^{*} \succsim_{\left(i, t_{i}\right)}^{*} b^{*}$ if and only if $L_{i}\left(a^{*}, t_{i}\right) \succsim_{i} L_{i}\left(b^{*}, t_{i}\right)$,
where $L_{i}\left(a^{*}, t_{i}\right)$ is the lottery over $A \times \Omega$ that assigns probability $p_{i}(\omega) / p_{i}\left(\tau_{i}^{-1}\left(t_{i}\right)\right)$ to $\left(\left(a^{*}\left(j, \tau_{j}(\omega)\right)\right)_{j \in N}, \omega\right)$ if $\omega \in \tau_{i}^{-1}\left(t_{i}\right)$, zero otherwise.

### 3.2. How to define Bayesian Game and Beyesian Nash Equilibrium? Find answers in “Multiagent Systems: Algorithmic, Game-Theoretic, and Logical Foundations” by Prof. Yoav Shoham and Kevin Leyton-Brown. ([Download](http://www.masfoundations.org/download.html))

Refer to Textbook: [Shoham, Yoav, and Kevin Leyton-Brown](http://www.masfoundations.org/download.html). 2008. Multiagent Systems: Algorithmic, Game-Theoretic, and Logical Foundations. Cambridge: Cambridge University Press. (Chapter 6, Page 167-170, Definition 6.3.2 & 6.3.7)

Definition 6.3.2 (Bayesian game: types) A Bayesian game is a tuple $(N, A, \Theta, p, u)$ where:
- $N$ is a set of agents;
- $A=A_{1} \times \cdots \times A_{n}$, where $A_{i}$ is the set of actions available to player $i$;
- $\Theta=\Theta_{1} \times \ldots \times \Theta_{n}$, where $\Theta_{i}$ is the type space of player $i$;
- $p: \Theta \mapsto[0,1]$ is a common prior over types; and
- $u=\left(u_{1}, \ldots, u_{n}\right)$, where $u_{i}: A \times \Theta \mapsto \mathbb{R}$ is the utility function for player $i$.
The assumption is that all of the above is common knowledge among the players, and that each agent knows his own type. This definition can seem mysterious, because the notion of type can be rather opaque. In general, the type of agent encapsulates all the information possessed by the agent that is not common knowledge. This is often quite simple (e.g., the agent's knowledge of his private payoff function), but can also include his beliefs about other agents' payoffs, about their beliefs about his own payoff, and any other higher-order beliefs.

We can get further insight into the notion of a type by relating it to the formulation at the beginning of this section. Consider again the Bayesian game in Figure 6.7. For each of the agents we have two types, corresponding to his two information sets. Denote player 1's actions as $\mathrm{U}$ and D, player 2's actions as $\mathrm{L}$ and R. Call the types of the first agent $\theta_{1,1}$ and $\theta_{1,2}$, and those of the second agent $\theta_{2,1}$ and $\theta_{2,2}$. The joint distribution on these types is as follows: $p\left(\theta_{1,1}, \theta_{2,1}\right)=.3$, $p\left(\theta_{1,1}, \theta_{2,2}\right)=.1, p\left(\theta_{1,2}, \theta_{2,1}\right)=.2, p\left(\theta_{1,2}, \theta_{2,2}\right)=.4$. The conditional probabilities for the first player are $p\left(\theta_{2,1} \mid \theta_{1,1}\right)=3 / 4, p\left(\theta_{2,2} \mid \theta_{1,1}\right)=1 / 4$, $p\left(\theta_{2,1} \mid \theta_{1,2}\right)=1 / 3$, and $p\left(\theta_{2,2} \mid \theta_{1,2}\right)=2 / 3$.

Definition 6.3.7 (Bayes-Nash equilibrium) A Bayes-Nash equilibrium is a mixedstrategy profile s that satisfies $\forall i \quad s_{i} \in B R_{i}\left(s_{-i}\right)$.

This is exactly the definition we gave for the Nash equilibrium in Definition 3.3.4: each agent plays a best response to the strategies of the other players. The difference from Nash equilibrium, of course, is that the definition of Bayes-Nash equilibrium is built on top of the Bayesian game definitions of best response and expected utility. Observe that we would not be able to define equilibrium in this way if an agent's strategies were not defined for every possible type. In order for a given agent $i$ to play a best response to the other agents $-i, i$ must know what strategy each agent would play for each of his possible types. Without this information, it would be impossible to evaluate the term $E U_{i}\left(s_{i}^{\prime}, s_{-i}\right)$ in Equation (6.7).

## 4. Extensive Form Game

### 4.1. How to define extensive form game? Find answers in “A Course in Game Theory by Prof. Ariel Rubinstein and M. Osborne. ([Download](https://arielrubinstein.tau.ac.il/books.html))

Refer to Textbook: [Osborne, Martin J. and Ariel Rubinstein](https://arielrubinstein.tau.ac.il/books.html). 1994. A Course in Game Theory. (Chapter 6, Page 89, DEFINITION 6.1.1)

An extensive game is a detailed description of the sequential structure of the decision problems encountered by the players in a strategic situation.

### 4.2. How to define extensive form game? Find answers in “Multiagent Systems: Algorithmic, Game-Theoretic, and Logical Foundations” by Prof. Yoav Shoham and Kevin Leyton-Brown. ([Download](http://www.masfoundations.org/download.html))

Refer to Textbook: [Noam and Nisan.](https://www.cs.cmu.edu/~sandholm/cs15-892F13/algorithmic-game-theory.pdf) 2007. Algorithmic Game Theory. (Page 53)

Extensive games are game trees, with information sets that model imperfect information of the players.

## 5. Subgame Perfect Nash Equilibrium

### 5.1 How to define Subgame Perfect Nash Equilibrium (SPNE)? Find answers in “A Course in Game Theory by Prof. Ariel Rubinstein and M. Osborne. ([Download](https://arielrubinstein.tau.ac.il/books.html))

Refer to Textbook: [Osborne, Martin J. and Ariel Rubinstein](https://arielrubinstein.tau.ac.il/books.html). 1994. A Course in Game Theory. (Chapter 6, Page 97, DEFINITION 97.2)

A subgame perfect equilibrium of an extensive game with perfect information $\Gamma=\left\langle N, H, P,\left(\succsim_{i}\right)\right\rangle$ is a strategy profile $s^{*}$ such that for every player $i \in N$ and every nonterminal history $h \in H \backslash Z$ for which $P(h)=i$ we have
$$
\left.O_{h}\left(\left.s_{-i}^{*}\right|_{h},\left.s_{i}^{*}\right|_{h}\right) \succsim_{i}\right|_{h} O_{h}\left(\left.s_{-i}^{*}\right|_{h}, s_{i}\right)
$$
for every strategy $s_{i}$ of player $i$ in the subgame $\Gamma(h)$.
Equivalently, we can define a subgame perfect equilibrium to be a strategy profile $s^{*}$ in $\Gamma$ for which for any history $h$ the strategy profile $\left.s^{*}\right|_{h}$ is a Nash equilibrium of the subgame $\Gamma(h)$.

The notion of subgame perfect equilibrium eliminates Nash equilibria in which the players' threats are not credible. For example, in the game in Figure $96.2$ the only subgame perfect equilibrium is $(A, R)$ and in the game in Figure $91.1$ the only subgame perfect equilibria are $((2,0), y y y)$ and $((1,1)$, nyy $)$.

### 5.2 How to define Subgame Perfect Nash Equilibrium (SPNE)? Find answers in “Multiagent Systems: Algorithmic, Game-Theoretic, and Logical Foundations” by Prof. Yoav Shoham and Kevin Leyton-Brown. ([Download](http://www.masfoundations.org/download.html))

Refer to Textbook: [Shoham, Yoav, and Kevin Leyton-Brown](http://www.masfoundations.org/download.html). 2008. Multiagent Systems: Algorithmic, Game-Theoretic, and Logical Foundations. Cambridge: Cambridge University Press. (Chapter 5, Page 123, Definition 5.1.5)

Definition 5.1.5 (Subgame-perfect equilibrium) The subgame-perfect equilibria $(S P E)$ of a game $G$ are all strategy profiles s such that for any subgame $G^{\prime}$ of $G$, the restriction of s to $G^{\prime}$ is a Nash equilibrium of $G^{\prime}$.

## 6. Perfect Bayesian Equilibrium

### 6.1 How to define Perfect Bayesian Equilibrium (PBE)? Find answers in “A Course in Game Theory by Prof. Ariel Rubinstein and M. Osborne. ([Download](https://arielrubinstein.tau.ac.il/books.html))

Refer to Textbook: [Osborne, Martin J. and Ariel Rubinstein](https://arielrubinstein.tau.ac.il/books.html). 1994. A Course in Game Theory. (Chapter 12, Page 232-233, DEFINITION 232.1)

Let $\left\langle\Gamma,\left(\Theta_{i}\right),\left(p_{i}\right),\left(u_{i}\right)\right\rangle$ be a Bayesian extensive game with observable actions, where $\Gamma=\langle N, H, P\rangle$. A pair $\left(\left(\sigma_{i}\right),\left(\mu_{i}\right)\right)=$ $\left(\left(\sigma_{i}\left(\theta_{i}\right)\right)_{i \in N, \theta_{i} \in \Theta_{i}},\left(\mu_{i}(h)\right)_{i \in N, h \in H \backslash Z}\right)$, where $\sigma_{i}\left(\theta_{i}\right)$ is a behavioral strategy of player $i$ in $\Gamma$ and $\mu_{i}(h)$ is a probability measure on $\Theta_{i}$, is a perfect Bayesian equilibrium of the game if the following conditions are satisfied.

- Sequential rationality: For every nonterminal history $h \in H \backslash Z$, every player $i \in P(h)$, and every $\theta_{i} \in \Theta_{i}$ the probability measure $O\left(\sigma_{-i}, \sigma_{i}\left(\theta_{i}\right), \mu_{-i} \mid h\right)$ is at least good for type $\theta_{i}$ as $O\left(\sigma_{-i}, s_{i}, \mu_{-i} \mid h\right)$ for any strategy $s_{i}$ of player $i$ in $\Gamma$.
- Correct initial beliefs: $\mu_{i}(\varnothing)=p_{i}$ for each $i \in N$.
- Action-determined beliefs: If $i \notin P(h)$ and $a \in A(h)$ then $\mu_{i}(h, a)=$ $\mu_{i}(h)$; if $i \in P(h), a \in A(h), a^{\prime} \in A(h)$, and $a_{i}=a_{i}^{\prime}$ then $\mu_{i}(h, a)=$ $\mu_{i}\left(h, a^{\prime}\right)$.
- Bayesian updating: If $i \in P(h)$ and $a_{i}$ is in the support of $\sigma_{i}\left(\theta_{i}\right)(h)$ for some $\theta_{i}$ in the support of $\mu_{i}(h)$ then for any $\theta_{i}^{\prime} \in \Theta_{i}$ we have
$$
\mu_{i}(h, a)\left(\theta_{i}^{\prime}\right)=\frac{\sigma_{i}\left(\theta_{i}^{\prime}\right)(h)\left(a_{i}\right) \cdot \mu_{i}(h)\left(\theta_{i}^{\prime}\right)}{\sum_{\theta_{i} \in \Theta_{i}} \sigma_{i}\left(\theta_{i}\right)(h)\left(a_{i}\right) \cdot \mu_{i}(h)\left(\theta_{i}\right)} .
$$

### 6.2 How to define Perfect Bayesian Equilibrium (PBE)? Find answers in “Multiagent Systems: Algorithmic, Game-Theoretic, and Logical Foundations” by Prof. Yoav Shoham and Kevin Leyton-Brown. ([Download](http://www.masfoundations.org/download.html))

Refer to Textbook: [Shoham, Yoav, and Kevin Leyton-Brown](http://www.masfoundations.org/download.html). 2008. Multiagent Systems: Algorithmic, Game-Theoretic, and Logical Foundations. Cambridge: Cambridge University Press. (Chapter 5, Page 144, Definition 5.2.10)

Definition 5.2.10 (Sequential equilibrium) A strategy profile $s$ is a sequential equilibrium of an extensive-form game $G$ if there exist probability distributions $\mu(h)$ for each information set $h$ in $G$, such that the following two conditions hold:
1. $(s, \mu)=\lim _{m \rightarrow \infty}\left(s^{m}, \mu^{m}\right)$ for some sequence $\left(s^{1}, \mu^{1}\right),\left(s^{2}, \mu^{2}\right), \ldots$, where $s^{m}$ is fully mixed, and $\mu^{m}$ is consistent with $s^{m}$ (in fact, since $s^{m}$ is fully mixed, $\mu^{m}$ is uniquely determined by $\left.s^{m}\right)$; and
2. For any information set $h$ belonging to agent $i$, and any alternative strategy $s_{i}^{\prime}$ of $i$, we have that
$$
u_{i}(s \mid h, \mu(h)) \geq u_{i}\left(\left(s^{\prime}, s_{-i}\right) \mid h, \mu(h)\right)
$$
Analogous to subgame-perfect equilibria in games of perfect information, sequential equilibria are guaranteed to always exist.

# Part II: Computational Issues (1 points)

### Brieftly read Algorithmic Game Theory e-book Chapter 1 and 2. ([URL](https://drive.google.com/file/d/1qbWzkgKbej9KTRcOOK7a57LEU83lRpfY/view?usp=sharing)) Answer the following questions:

### RQ1: What is the computational issues of the Nash Equilibrium?

1. No Nash equilibria: Even though mixed-strategy Nash equilibrium exists for a game with a finite number of players and a finite number of actions, a game with an infinite number of players, or a game with a finite number of players with an infinite strategy set may not have Nash equilibria[(Noam Nisan 2007, 13)](https://www.cs.cmu.edu/~sandholm/cs15-892F13/algorithmic-game-theory.pdf). This reminds me of the intermediate microeconomic class. When we tried to find the optimal solutions for a given utility function and choice set, even though in most cases they have mathematical optimal solutions, we should be careful about some special cases, such as the boundary solutions. While we are appreciating theoretical models, we should also be cautious about how it interacts with and informs about the real world.

2. Multiple Nash equilibria: Even though in many cases Nash equilibria could be computed, it is not strange that for some games there exist mutiple Nash Equilibria and we cannot explicitly tell which equilibria to be selected. In the case of multiple Nash equilibria, the solutions are less useful and directional in predicting players' decision-making and behaviors. As a result, it is even harder for players to know which equilibrium they are supposed to coordinate on [(Noam Nisan 2007, 12)](https://www.cs.cmu.edu/~sandholm/cs15-892F13/algorithmic-game-theory.pdf). This also reminds me of the models in intermediate microeconomic class. When either the choice set or the utility function is not convex, there might exist mutiple solutions and we have to look at them case by case.

### RQ2: What is the complexity of finding Nash Equilibrium?

In the case of a two-player game, applying the Lemke–Howson algorithm, we are able to compute a Nash Equilibrium in exponential time at worst. NP completeness, the “standard” way of establishing intractability of individual problems, is not appropriate because Nash (1951) proved that Nash equilibria always exist. The problem of finding a Nash equilibrium is PPAD-complete even for two-player games in standard form[(Noam Nisan 2007, 16)](https://www.cs.cmu.edu/~sandholm/cs15-892F13/algorithmic-game-theory.pdf). The correlated equilibrium is a computationally benign generalization of the intractable Nash equilibrium. We can find in polynomial time a correlated equilibrium for any game by linear programming techniques [(Noam Nisan 2007, 47)](https://www.cs.cmu.edu/~sandholm/cs15-892F13/algorithmic-game-theory.pdf).

*   [NP completeness](https://www.britannica.com/science/NP-complete-problem)
> A problem is called NP (nondeterministic polynomial) if its solution can be guessed and verified in polynomial time; nondeterministic means that no particular rule is followed to make the guess. If a problem is NP and all other NP problems are polynomial-time reducible to it, the problem is NP-complete.

*   [PPAD](https://en.wikipedia.org/wiki/PPAD_(complexity))
>In computer science, PPAD ("Polynomial Parity Arguments on Directed graphs") is a complexity class introduced by Christos Papadimitriou in 1994.

# Part III: Case Studies (8 points)

## 1.NashPy

### 1.1. What types of games and solution concepts can NashPy deal with?

Nashpy is a python library for the computation of equilibria of 2 player strategic games.

[(Knight 2021)](https://github.com/drvinceknight/Nashpy)

### 1.2. Apply NashPy to solve a solution concept on a specific game (your own choices) different from the case studies in the OER

Rock Paper Scissors is a hand game originating from China, usually played between two people, in which each player simultaneously forms one of three shapes with an outstretched hand.

A simultaneous, zero-sum game, it has three possible outcomes: a draw, a win or a loss. A player who decides to play rock will beat another player who has chosen scissors ("rock crushes scissors" or "breaks scissors" or sometimes "blunts scissors"), but will lose to one who has played paper ("paper covers rock"); a play of paper will lose to a play of scissors ("scissors cuts paper"). If both players choose the same shape, the game is tied and is usually immediately replayed to break the tie. The type of game originated in China and spread with increased contact with East Asia, while developing different variants in signs over time.

[(“Rock Paper Scissors” 2020)](https://en.wikipedia.org/wiki/Rock_paper_scissors)

```python
# install the tools you will use later
!pip install --upgrade setuptools
!pip install --upgrade pip
!pip install quantecon
```

    Requirement already satisfied: setuptools in /usr/local/lib/python3.7/dist-packages (62.1.0)
    WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
    Requirement already satisfied: pip in /usr/local/lib/python3.7/dist-packages (22.0.4)
    WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
    Requirement already satisfied: quantecon in /usr/local/lib/python3.7/dist-packages (0.5.3)
    Requirement already satisfied: scipy>=1.0.0 in /usr/local/lib/python3.7/dist-packages (from quantecon) (1.4.1)
    Requirement already satisfied: numba in /usr/local/lib/python3.7/dist-packages (from quantecon) (0.51.2)
    Requirement already satisfied: sympy in /usr/local/lib/python3.7/dist-packages (from quantecon) (1.7.1)
    Requirement already satisfied: numpy in /usr/local/lib/python3.7/dist-packages (from quantecon) (1.21.5)
    Requirement already satisfied: requests in /usr/local/lib/python3.7/dist-packages (from quantecon) (2.23.0)
    Requirement already satisfied: setuptools in /usr/local/lib/python3.7/dist-packages (from numba->quantecon) (62.1.0)
    Requirement already satisfied: llvmlite<0.35,>=0.34.0.dev0 in /usr/local/lib/python3.7/dist-packages (from numba->quantecon) (0.34.0)
    Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.7/dist-packages (from requests->quantecon) (2021.10.8)
    Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from requests->quantecon) (3.0.4)
    Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /usr/local/lib/python3.7/dist-packages (from requests->quantecon) (1.24.3)
    Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.7/dist-packages (from requests->quantecon) (2.10)
    Requirement already satisfied: mpmath>=0.19 in /usr/local/lib/python3.7/dist-packages (from sympy->quantecon) (1.2.1)
    WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv

```python
!which python
```

    /usr/local/bin/python

```python
!pip3 install nashpy --use-deprecated=backtrack-on-build-failures
```

    DEPRECATION: Backtracking on build failures can mask issues related to how a package generates metadata or builds a wheel. This flag will be removed in pip 22.2. A possible replacement is avoiding known-bad versions by explicitly telling pip to ignore them (either directly as requirements, or via a constraints file). Discussion can be found at https://github.com/pypa/pip/issues/10655
    Collecting nashpy
      Using cached nashpy-0.0.22.tar.gz (11 kB)
      error: subprocess-exited-with-error

      × python setup.py egg_info did not run successfully.
      │ exit code: 1
      ╰─> See above for output.

      note: This error originates from a subprocess, and is likely not a problem with pip.
      Preparing metadata (setup.py) ... error
    WARNING: Discarding https://files.pythonhosted.org/packages/93/1c/9005c6a0a3a3b183b5b216cc02cef14bb080fd3ee8733a1006fbcd81fffc/nashpy-0.0.22.tar.gz#sha256=9378fd492f01163ac01a7384dd94f7ffa3ce40e31fe2a08844a2e34576b3d8db (from https://pypi.org/simple/nashpy/) due to build failure: metadata generation failed
      Downloading nashpy-0.0.21.tar.gz (11 kB)
      Preparing metadata (setup.py) ... done
    Requirement already satisfied: numpy>=1.12.1 in /usr/local/lib/python3.7/dist-packages (from nashpy) (1.21.5)
    Requirement already satisfied: scipy>=0.19.0 in /usr/local/lib/python3.7/dist-packages (from nashpy) (1.4.1)
    Building wheels for collected packages: nashpy
      Building wheel for nashpy (setup.py) ... done
      Created wheel for nashpy: filename=nashpy-0.0.21-py3-none-any.whl size=15279 sha256=d6a0755aa742f535a94d26247d80a704995b49b6d60c73263a5ffc51c4fa3cb7
      Stored in directory: /root/.cache/pip/wheels/02/08/62/cf4fa931e0a317d180936b266169a57f4bb4eb801465bbe8a1
    Successfully built nashpy
    Installing collected packages: nashpy
    Successfully installed nashpy-0.0.21
    WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv

```python
import nashpy as nash
import numpy as np

# Creater the game with the payoff matrix

A = np.array([[0, 0, 1],
              [1, 0, 0],
              [0, 1, 0]]) # A is the row player (in this case, prisoner)

B = np.array([[0, 1, 0],
              [0, 0, 1],
              [1, 0, 0]]) # B is the column player
```

```python
# Form the game
game2 = nash.Game(A,B)
game2
```

    Bi matrix game with payoff matrices:

    Row player:
    [[0 0 1]
     [1 0 0]
     [0 1 0]]

    Column player:
    [[0 1 0]
     [0 0 1]
     [1 0 0]]

```python
# Find the Nash Equilibrium with Support Enumeration
equilibria = game2.support_enumeration()
for eq in equilibria:
    print(eq)
```

    (array([0.33333333, 0.33333333, 0.33333333]), array([0.33333333, 0.33333333, 0.33333333]))

The results are consistent with the facts and are explainable. This game has a mixed Nash equilibrium. In this equilibium, both players play the mixed strategy that puts equal probabilities on all three actions. In short words, they play rock, paper, or scissors with the equal frequency (probability).

## 2. QuantEcon

### 2.1. What types of games and soution concepts can QuantEcon deal with?

QuantEcon is the open source code for economic modeling. It can deal with normal form game with more than 2 players and solve for the Nash Equilibrium.
There are several algorithms implemented to compute Nash equilibria:

*   Brute force
> Find all pure-action Nash equilibria of an N-player game (if any).
*   Sequential best response
> Find one pure-action Nash equilibrium of an N-player game (if any).
*   Support enumeration
> Find all mixed-action Nash equilibria of a two-player nondegenerate game.
*   Vertex enumeration
> Find all mixed-action Nash equilibria of a two-player nondegenerate game.
*   Lemke-Howson
> Find one mixed-action Nash equilibrium of a two-player game.
*   McLennan-Tourky
> Find one mixed-action Nash equilibrium of an N-player game.

QuantEcon is also developing Julia code that implements an algorithm for computing equilibrium values (e.g. subgame perfect equilibria) and associated equilibrium strategy profiles for repeated games with N players and finite actions.

[(Sargent and Stachurski 2021)](https://quantecon.org/notebooks/)

### 2.2. Apply QuantEcon to solve a solution concept on a specific game (your own choices) different from the case studies in the OER

Rock Paper Scissors is a hand game originating from China, usually played between two people, in which each player simultaneously forms one of three shapes with an outstretched hand.

A simultaneous, zero-sum game, it has three possible outcomes: a draw, a win or a loss. A player who decides to play rock will beat another player who has chosen scissors ("rock crushes scissors" or "breaks scissors" or sometimes "blunts scissors"), but will lose to one who has played paper ("paper covers rock"); a play of paper will lose to a play of scissors ("scissors cuts paper"). If both players choose the same shape, the game is tied and is usually immediately replayed to break the tie. The type of game originated in China and spread with increased contact with East Asia, while developing different variants in signs over time.

[(“Rock Paper Scissors” 2020)](https://en.wikipedia.org/wiki/Rock_paper_scissors)

```python
# import the QuantEcon package
import quantecon.game_theory as gt
import numpy as np

# The first way to form the game: by the payoff matrix
prisoner_dilemma_matrix = np.array([[[0, 0],  [0, 1],  [1, 0]],
                                    [[1, 0],  [0, 0],  [0, 1]],
                                    [[0, 1],  [1, 0],  [0, 0]]])

g_PD = gt.NormalFormGame(prisoner_dilemma_matrix)
print(g_PD)
```

```python
# Finding the Nash equilibrium: pure_nash_brute

NE = gt.pure_nash_brute(g_PD)
print(NE)
```

```python
# Finding the Nash equilibrium: support_enumeration

NE = gt.support_enumeration(g_PD)
print(NE)
```

```python
NE = gt.vertex_enumeration(g_PD)
print(NE)
```

The results are consistent with the facts and are explainable. The game of Rock-Paper-Scissors doesn't have pure Nash equilibrium. Imagine you are playing rock all the time, once the other player figures out your strategy and he/she can win by always playing paper. However, this game has a unique mixed Nash equilibrium. In this equilibium, both players play the mixed strategy that puts equal probabilities on all three actions. In short words, they play rock, paper, or scissors with the equal frequency (probability).

## 3. Game Theory Explorer

### 3.1. What types of games and soution concepts can Game Theory Explorer deal with?

Game theory explorer is a web interface to gambit useful for teaching. An extensive or strategic-form game can be created and nicely displayed with a graphical user interface in a web browser. State-of-the-art algorithms then compute one or all Nash equilibria of the game.

[(Savani and von Stengel 2014)](https://gte.csc.liv.ac.uk/index/)

### 3.2. Apply Game Theory Explorer to solve a solution concept on a specific game (your own choices) different from the case studies in the OER

Rock Paper Scissors is a hand game originating from China, usually played between two people, in which each player simultaneously forms one of three shapes with an outstretched hand.

A simultaneous, zero-sum game, it has three possible outcomes: a draw, a win or a loss. A player who decides to play rock will beat another player who has chosen scissors ("rock crushes scissors" or "breaks scissors" or sometimes "blunts scissors"), but will lose to one who has played paper ("paper covers rock"); a play of paper will lose to a play of scissors ("scissors cuts paper"). If both players choose the same shape, the game is tied and is usually immediately replayed to break the tie. The type of game originated in China and spread with increased contact with East Asia, while developing different variants in signs over time.

[(“Rock Paper Scissors” 2020)](https://en.wikipedia.org/wiki/Rock_paper_scissors)

(1) General Matrix

(2) Strategic Form

（3）Extensive Form

The results are consistent with the facts and are explainable. The game of Rock-Paper-Scissors doesn't have pure Nash equilibrium. Imagine you are playing rock all the time, once the other player figures out your strategy and he/she can win by always playing paper. However, this game has a unique mixed Nash equilibrium. In this equilibium, both players play the mixed strategy that puts equal probabilities on all three actions. In short words, they play rock, paper, or scissors with the equal frequency (probability).

## 4. Gambit

### 4.1. What types of games and solution concepts can Gambit deal with?

Gambit is a library of game theory software and tools for the construction and analysis of finite extensive and strategic noncooperative games. Gambit supports building game trees, finding all equilibria of a two-player game, computing equilibria in games with three or more players, and doing statistical game theory. It is more powerful and includes more various games than QuantEcon.

[(McKelvey, McLennan, and Turocy 2016)]( http://www.gambit-project.org)

### 4.2. Apply Gambit to solve a solution concept on a specific game (your own choices) different from the case studies in the OER

**Poker Game - Description**

The game begins with each player being dealt two cards which are hidden from the other player.
A round of betting takes place, where there are four actions available to the players: check, bet, call, raise. A player can check or bet if no amount has yet been made in the current round of betting and a player can call (match the amount bet by the opponent) or raise (bet an additional amount on top of opponent’s bet) if the opponent bets. After the initial round of betting (pre-flop), the first three community cards (visible to both players) come out (flop). Another round of betting proceeds before the fourth card comes out and likewise before the fifth and final card. After all cards are out, there is one last round of betting before the players’ hands are compared (showdown). The complexity of poker arises from inferring probabilities through the many rounds of betting and making decisions that consider events in the future.

[(Li 2018)](https://math.mit.edu/~apost/courses/18.204_2018/Jingyu_Li_paper.pdf)

Gambit can show various games in several forms, including the graphical form. The poker game's rule is not that complex, but with the help of Gambit, it is easier and clearer to analyze the decision tree and unfold the strategies. Besides, Gambit users can change the color to adjust the plot accordingly.

# 5. Mesa [optional]

## 5.1. What types of games and solution concepts can Mesa deal with?

It allows users to quickly create agent-based models using built-in core components (such as spatial grids and agent schedulers) or customized implementations; visualize them using a browser-based interface; and analyze their results using Python’s data analysis tools.
Agent-based models are computer simulations involving multiple entities (the agents) acting and interacting with one another based on their programmed behavior. Agents can be used to represent living cells, animals, individual humans, even entire organizations or abstract entities. Sometimes, we may have an understanding of how the individual components of a system behave, and want to see what system-level behaviors and effects emerge from their interaction. Other times, we may have a good idea of how the system overall behaves, and want to figure out what individual behaviors explain it. Or we may want to see how to get agents to cooperate or compete most effectively. Or we may just want to build a cool toy with colorful little dots moving around.

[(Thomson 2020)](https://mesa.readthedocs.io/en/latest/index.html)

# References

Noam Nisan. 2007. Algorithmic Game Theory. Cambridge ; New York: Cambridge University Press.

Osborne, Martin J, and Ariel Rubinstein. 1994. A Course in Game Theory. Cambridge, Mass.: Mit Press.

Yoav Shoham, and Kevin Leyton-Brown. 2009. Multiagent Systems : Algorithmic, Game-Theoretic, and Logical Foundations. Cambridge ; New York: Cambridge University Press.

Kazil, Jackie, Masad, David, Crooks, and Andrew. 2013. “Mesa: Agent-Based Modeling in Python 3+.” GitHub. 2013. https://github.com/projectmesa/mesa.Knight.

Vincent. 2021. “Nashpy: A Python Library for the Computation of Equilibria of 2 Player Strategic Games, Version 0.0.28.” GitHub. May 4, 2021. https://github.com/drvinceknight/Nashpy.McKelvey.

Richard, Andrew McLennan, and Theodore Turocy. n.d. “Gambit: Software Tools for Game Theory, Version 16.0.1.” Www.gambit-Project.org. http://www.gambit-project.org/.Sargent.

Thomas, and John Stachurski. 2021. “Quantitative Economics (Python), Version 0.5.1.” QuantEcon. 2021. https://quantecon.org/quantecon-py/.Savani.

Rahul, and Bernhard von Stengel. 2014. “Game Theory Explorer: Software for the Applied Game Theorist.” Computational Management Science 12 (1): 5–33. https://doi.org/10.1007/s10287-014-0206-x.

Li, Jingyu. 2018. “Exploitability and Game Theory Optimal Play in Poker.” Boletín de Matemáticas 0 (0): 1. https://math.mit.edu/~apost/courses/18.204_2018/

Jingyu_Li_paper.pdf.“Rock Paper Scissors.” 2020. Wikipedia. August 21, 2020. https://en.wikipedia.org/wiki/Rock_paper_scissors.

```python

```