Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Table of Contents

Imports

!conda info --envs
# conda environments:
#
deeplearning             /Users/gp/.conda/envs/deeplearning
jupyter               *  /Users/gp/.conda/envs/jupyter
particleone_teza         /Users/gp/.conda/envs/particleone_teza
statarbdeps              /Users/gp/.conda/envs/statarbdeps
statarbdeps.20170712-14_11_50     /Users/gp/.conda/envs/statarbdeps.20170712-14_11_50
statarbdeps.20170713-14_06_23     /Users/gp/.conda/envs/statarbdeps.20170713-14_06_23
statarbdeps_base         /Users/gp/.conda/envs/statarbdeps_base
statarbdeps_base.20171204-13_21_50     /Users/gp/.conda/envs/statarbdeps_base.20171204-13_21_50
statarbdeps_gp           /Users/gp/.conda/envs/statarbdeps_gp
tzdataetlgreen           /Users/gp/.conda/envs/tzdataetlgreen
root                     /opt/teza-conda

import functools

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

%matplotlib inline
import notebook_utils

notebook_utils.notebook_config()
# matplotlib.rcParams["figure.figsize"] = (15, 6)

# assert np.__version__ == "1.33.3"
assert pd.__version__ == "0.20.3"
# assert matplotlib.__version__ == "2.0.2"
numpy= 1.13.3
pandas= 0.20.3
matplotlib= 2.0.2
sklearn= 0.18.1
from notebook_utils import display_df

from IPython.display import display

# def display_df(df):
#     display(df)

TODOs

Selection

.loc, .iloc, .ix

# .loc[] vs .ix[] vs .iloc[]

# .ix supports mixed integer and label based access
# It is primarily label based, but will fall back to integer positional access unless the
# corresponding axis is of integer type.
# .ix is the most general and will support any of the inputs in .loc and .iloc.
# .ix also supports floating point label schemes.
# .ix is exceptionally useful when dealing with mixed positional and label based hierachical indexes.

# When an axis is integer based, ONLY label based access and not positional access is supported.
# Thus, in such cases, it’s usually better to be explicit and use .iloc or .loc.
# TODO: Add examples.
df = pd.DataFrame(
    [[1, 2, 0], [3, 4, 0], [5, 6, 0]],
    index="a b c".split(),
    columns="A B C".split(),
)
df
Loading...
df.xs("a", axis=0)
A 1 B 2 C 0 Name: a, dtype: int64
df.xs("A", axis=1)
a 1 b 3 c 5 Name: A, dtype: int64
df.xs(["A", "B"], axis=1)
Loading...
# ?
df.xs(slice("A", "C"), axis=1)
Loading...
df.loc["a"]
A 1 B 2 C 0 Name: a, dtype: int64
df["A"]
a 1 b 3 c 5 Name: A, dtype: int64
df.iloc[0]
A 1 B 2 C 0 Name: a, dtype: int64

Selecting rows

df_orig = pd.DataFrame(
    {"AAA": [4, 5, 6, 7], "BBB": [10, 20, 30, 40], "CCC": [100, 50, -30, -50]}
)
# These are equivalent syntax.
print(df_orig["AAA"])

print(df_orig.AAA)
0    4
1    5
2    6
3    7
Name: AAA, dtype: int64
0    4
1    5
2    6
3    7
Name: AAA, dtype: int64
df = df_orig.copy()
df
Loading...
# Select a piece of the df and overwrite part of it.
# .ix[] is the same as .loc[]
df.ix[df.AAA >= 5, "BBB"] = -1
df
/Users/gp/.conda/envs/jupyter/lib/python2.7/site-packages/ipykernel_launcher.py:3: DeprecationWarning: 
.ix is deprecated. Please use
.loc for label based indexing or
.iloc for positional indexing

See the documentation here:
http://pandas.pydata.org/pandas-docs/stable/indexing.html#ix-indexer-is-deprecated
  This is separate from the ipykernel package so we can avoid doing imports until
Loading...
# Select a piece of the df and overwrite part of it.
df.loc[df.AAA >= 5, "BBB"] = -1
df
Loading...
# Select a piece of df and overwrite it.
df.ix[df.AAA >= 5, ["BBB", "CCC"]] = 555
df
Loading...
# Selecting a df through a mask.
df = df_orig.copy()
mask = pd.DataFrame(
    {"AAA": [True] * 4, "BBB": [False] * 4, "CCC": [True, False] * 2}
)
mask
Loading...
# Filter df using df.where() and a boolean mask.
# nan is returned where the mask is False, the value of the df
# where the mask is True.
df.where(mask)
Loading...
# Return a different value for when the mask is False.
df.where(mask, other="high")
Loading...
# Use np.where().

column = df.AAA
np.where(column, "high", "low")
array(['high', 'high', 'high', 'high'], dtype='|S4')
# Select rows based on certain criteria.
df = df_orig.copy()

dflow = df[df.AAA <= 5]
dfhigh = df[df.AAA > 5]

print(dflow)
print(dfhigh)
   AAA  BBB  CCC
0    4   10  100
1    5   20   50
   AAA  BBB  CCC
2    6   30  -30
3    7   40  -50
mask = (df.BBB < 25) & (df.CCC >= -40)
df.loc[mask, "AAA"]
0 4 1 5 Name: AAA, dtype: int64
df.loc[mask, "AAA":"BBB"]
Loading...
df[mask][["AAA", "BBB"]]
Loading...
df.ix[(df.CCC - 43.0).abs().argsort()]
/Users/gp/.conda/envs/jupyter/lib/python2.7/site-packages/ipykernel_launcher.py:1: DeprecationWarning: 
.ix is deprecated. Please use
.loc for label based indexing or
.iloc for positional indexing

See the documentation here:
http://pandas.pydata.org/pandas-docs/stable/indexing.html#ix-indexer-is-deprecated
  """Entry point for launching an IPython kernel.
Loading...
c1 = df.AAA <= 5.5
c2 = df.BBB == 10.0
c3 = df.CCC > -40.0
cs = [c1, c2, c3]
print(functools.reduce(lambda x, y: x & y, cs))

print(c1 & c2 & c3)
0     True
1    False
2    False
3    False
dtype: bool
0     True
1    False
2    False
3    False
dtype: bool
df.index.isin([0, 2, 4])
array([ True, False, True, False], dtype=bool)
df = df_orig.copy()
df = pd.DataFrame(
    df.values, index=["foo", "bar", "boo", "kar"], columns=df.columns
)
df
Loading...
df.loc["bar":"kar"]
Loading...
df.ix[0:3]
/Users/gp/.conda/envs/jupyter/lib/python2.7/site-packages/ipykernel_launcher.py:1: DeprecationWarning: 
.ix is deprecated. Please use
.loc for label based indexing or
.iloc for positional indexing

See the documentation here:
http://pandas.pydata.org/pandas-docs/stable/indexing.html#ix-indexer-is-deprecated
  """Entry point for launching an IPython kernel.
Loading...
df.iloc[0:3]
Loading...

Selecting columns

df = df_orig.copy()
df
Loading...
# Select columns where the column-wise column is larger than 30.
col_mask = df.sum() > 30

df.loc[:, col_mask]
Loading...

Find minimum

df = pd.DataFrame(
    {"AAA": [1, 1, 1, 2, 2, 2, 3, 3], "BBB": [2, 1, 3, 4, 5, 1, 2, 3]}
)

df
Loading...
# Find index of the min of one column.
np.argmin(df["BBB"])
1
# Find rows where there is a minimum of df["BBB"].
mask = df["BBB"] == np.min(df["BBB"])
df[mask]
Loading...
df = pd.DataFrame(
    {"AAA": [1, 2, 1, 3], "BBB": [1, 1, 2, 2], "CCC": [2, 1, 3, 1]}
)
df["CCC"].idxmin()
1
# groupby AAA values and find the min in BBB.
df.groupby("AAA")["BBB"].idxmin()
AAA 1 0 2 1 3 3 Name: BBB, dtype: int64
df.sort_values(by=["BBB", "CCC"])
Loading...

Adding columns

mask = df["AAA"] == np.min(df["AAA"])
df[mask]
Loading...

Using applymap()

  • applymap() applies a function to each element of a df returning another df.
# Add new cols converting numbers into greek letters.
df = pd.DataFrame(
    {"AAA": [1, 2, 1, 3], "BBB": [1, 1, 2, 2], "CCC": [2, 1, 3, 1]}
)
print("before=\n", df.to_string())

source_cols = df.columns
new_cols = [str(x) + "_cat" for x in source_cols]
categories = {1: "Alpha", 2: "Beta", 3: "Charlie"}
df[new_cols] = df[source_cols].applymap(lambda x: categories[x])
print("after=\n", df.to_string())
before=
   AAA  BBB  CCC
0    1    1    2
1    2    1    1
2    1    2    3
3    3    2    1
after=
   AAA  BBB  CCC  AAA_cat BBB_cat  CCC_cat
0    1    1    2    Alpha   Alpha     Beta
1    2    1    1     Beta   Alpha    Alpha
2    1    2    3    Alpha    Beta  Charlie
3    3    2    1  Charlie    Beta    Alpha

MultiIndex

  • A multi-index is just a subset of the cartesian product of values

Example with multi-index columns

# This df uses a _ to separate and encode conceptually the multi-index.
# Construct the df by columns.
df = pd.DataFrame(
    {
        "row": [0, 1, 2],
        "One_X": [1.1, 1.1, 1.1],
        "One_Y": [1.2, 1.2, 1.2],
        "Two_X": [1.11, 1.11, 1.11],
        "Two_Y": [1.22, 1.22, 1.22],
    }
)
df = df.set_index("row")
display_df(df)

# print "columns=", df.columns
# print "index=", df.index
Loading...
# Split the columns into tuples.
new_cols = [c.split("_") for c in df.columns]
print("new_cols=", new_cols)
tuples = list(map(tuple, new_cols))
print("tuples=", tuples)
new_cols= [['One', 'X'], ['One', 'Y'], ['Two', 'X'], ['Two', 'Y']]
tuples= [('One', 'X'), ('One', 'Y'), ('Two', 'X'), ('Two', 'Y')]
# Convert the columns into a multi-index.
midx = pd.MultiIndex.from_tuples(tuples)
print("midx=", midx)
df2 = df.copy()
df2.columns = midx

# Note that the multi-index is represented as a cartesian product of labels
# (each of them associated to an index for uniqueness).
display_df(df2)
midx= MultiIndex(levels=[[u'One', u'Two'], [u'X', u'Y']],
           labels=[[0, 0, 1, 1], [0, 1, 0, 1]])
Loading...
# stack() moves a level of column multi-indexing to the index (i.e., pivot)
df3 = df2.stack(0)
# print "columns=", df3.columns
# print "index=", df3.index
df3.head()
Loading...
# reset_index() converts a level of indexing into a column.
# Conceptually reset_index() is the opposite of set_index() using multiple columns.
df4 = df3.reset_index(1)

df4
Loading...
df5 = df3.reset_index(0)

df5
columns= Index([u'row', u'X', u'Y'], dtype='object')
index= Index([u'One', u'Two', u'One', u'Two', u'One', u'Two'], dtype='object')
Loading...

Simple example of stack()

  • stack() simply pivot a level of (possibly hierarchical) column labels, returning a DataFrame or Series having a hierarchical index with a new inner-most level
# help(pd.DataFrame.stack)
df = pd.DataFrame([[1, 2], [3, 4]], index=["one", "two"], columns=["a", "b"])
df
Loading...
# stack() moves the column index into rows getting a pd.Series.
srs = df.stack()
print("srs=\n", srs)

print("\nindex=\n", srs.index)
srs=
one  a    1
     b    2
two  a    3
     b    4
dtype: int64

index=
MultiIndex(levels=[[u'one', u'two'], [u'a', u'b']],
           labels=[[0, 0, 1, 1], [0, 1, 0, 1]])

Example of multi-index index

np.random.seed(42)
df = pd.DataFrame(np.random.randint(0, 10, (5, 3)), columns="a b c".split())

display(df)
Loading...
tuples = [(x, y) for x in "A B C".split() for y in "O I".split()]
print("tuples=", tuples)

cols = pd.MultiIndex.from_tuples(tuples)
print("cols=", cols)
tuples= [('A', 'O'), ('A', 'I'), ('B', 'O'), ('B', 'I'), ('C', 'O'), ('C', 'I')]
cols= MultiIndex(levels=[[u'A', u'B', u'C'], [u'I', u'O']],
           labels=[[0, 0, 1, 1, 2, 2], [1, 0, 1, 0, 1, 0]])
# To get the values of the multi-index columns.
cols.get_values()
array([('A', 'O'), ('A', 'I'), ('B', 'O'), ('B', 'I'), ('C', 'O'), ('C', 'I')], dtype=object)
# Build a df with the given multi-index as columns.
np.random.seed(1000)
df = pd.DataFrame(
    np.random.randn(2, len(cols.get_values())), index="n m".split(), columns=cols
)
df
Loading...
# Broadcast each level.
df.div(df["C"], level=1)
Loading...

Slicing using xs

coords = [
    ("AA", "one"),
    ("AA", "six"),
    ("BB", "one"),
    ("BB", "two"),
    ("BB", "six"),
]

# Use multi-index index.
index = pd.MultiIndex.from_tuples(coords)

df = pd.DataFrame([11, 22, 33, 44, 55], index, columns=["data"])
df
Loading...
# Select a row with .loc
df.loc["BB"]
Loading...
# Select a row with xs using innermost index.
df.xs("BB", level=0, axis=0)
Loading...
# Select a row using outermost index.
df.xs("six", level=1, axis=0)
Loading...

Slicing with loc

loc accepts rows and columns and they can be specified as tuples with slices

import itertools

# Build multi-index for index (using Cartesian product).
index = list(
    itertools.product(["Ada", "Quinn", "Violet"], ["Comp", "Math", "Sci"])
)
print("index=", index)

index = pd.MultiIndex.from_tuples(index, names=["Student", "Course"])
print("midx=", index)
index= [('Ada', 'Comp'), ('Ada', 'Math'), ('Ada', 'Sci'), ('Quinn', 'Comp'), ('Quinn', 'Math'), ('Quinn', 'Sci'), ('Violet', 'Comp'), ('Violet', 'Math'), ('Violet', 'Sci')]
midx= MultiIndex(levels=[[u'Ada', u'Quinn', u'Violet'], [u'Comp', u'Math', u'Sci']],
           labels=[[0, 0, 0, 1, 1, 1, 2, 2, 2], [0, 1, 2, 0, 1, 2, 0, 1, 2]],
           names=[u'Student', u'Course'])
# Build another multi-index for columns.
columns = list(itertools.product(["Exams", "Labs"], ["I", "II"]))
print("columns=", columns)

# Cols are not named.
cols = pd.MultiIndex.from_tuples(columns)
print("midx=", cols)
columns= [('Exams', 'I'), ('Exams', 'II'), ('Labs', 'I'), ('Labs', 'II')]
midx= MultiIndex(levels=[[u'Exams', u'Labs'], [u'I', u'II']],
           labels=[[0, 0, 1, 1], [0, 1, 0, 1]])
# Build df.
data = [
    [70 + x + y + (x * y) % 3 for x in range(len(cols))]
    for y in range(len(index))
]
df = pd.DataFrame(data, index, cols)
df
Loading...
# Select one row.
df.loc["Violet"]
Loading...
# Select various cubes.
all_ = slice(None)
df.loc[(all_, "Math"), all_]
Loading...
df.loc[(slice("Ada", "Violet"), "Math"), all_]
Loading...
df.loc[(all_, "Math"), ("Exams")]
Loading...
df.loc[(all_, "Math"), (all_, "II")]
Loading...

Misc ops

Sorting

df
Loading...
# Sort by last columns.
df.sort_values(by=("Labs", "II"), ascending=False)
Loading...

Levels

np.random.seed(1000)
df = pd.DataFrame(
    {
        "A": "a1 a1 a2 a3".split(),
        "B": "b1 b2 b3 b4".split(),
        "Vals": np.random.rand(4),
    }
)
df
Loading...
# Group by column "A" and sum.
df.groupby("A").sum()
Loading...
# Split by two columns.
df.groupby("A B".split()).sum()
Loading...
df["firstLevel"] = "foo"
df
Loading...
df.set_index("firstLevel")
Loading...
df2 = df.groupby("A B".split()).sum()
df2["firstLevel"] = "foo"
df2 = df2.set_index("firstLevel", append=True)

# reorder_levels() reorder the levels in an index.
df2.reorder_levels(["firstLevel", "A", "B"])
Loading...

apply(), applymap()

df = pd.DataFrame([[5, 6, 7], [0, 1, 2]], columns=list("abc"), index=list("12"))
df
Loading...
df.applymap(lambda x: x + 1)
Loading...
def reduce_(x):
    return sum(x)


display_df(df)
print(df.apply(reduce_, axis=0))
print(df.apply(reduce_, axis=1))
Loading...
a    5
b    7
c    9
dtype: int64
1    18
2     3
dtype: int64

Missing data

np.random.seed(1000)

index = "a c e f h".split()
print("index", index)

columns = "one two three".split()
print("cols", columns)

df = pd.DataFrame(np.random.randn(5, 3), index=index, columns=columns)
display_df(df)
index ['a', 'c', 'e', 'f', 'h']
cols ['one', 'two', 'three']
Loading...
# Add column.
df["four"] = "bar"
df
Loading...
# Add column function of another column.
df["five"] = df["one"] > 0
df
Loading...
# Add more indices by re-indexing.
df2 = df.reindex("a b c d e f g h".split())
df2
Loading...
df2["one"].isnull()
a False b True c False d True e False f False g True h False Name: one, dtype: bool
df2["one"].notnull()
a True b False c True d False e True f True g False h True Name: one, dtype: bool
df2.empty
False
df2.isnull()
Loading...
print("None == None:", None == None)
print("np.nan == np.nan:", np.nan == np.nan)
None == None: True
np.nan == np.nan: False
# Can't check for nan-ness by comparison.
# Need to use isnull() or np.isnan().
df2["one"] == np.nan
a False b False c False d False e False f False g False h False Name: one, dtype: bool

Datetimes

df2["timestamp"] = pd.Timestamp("20120101")
display_df(df2)
print(df.dtypes)
Loading...
one      float64
two      float64
three    float64
four      object
five        bool
dtype: object
pd.Timestamp("2012-01-01 05:00", tz="EST")
Timestamp('2012-01-01 05:00:00-0500', tz='EST')
# Setting a nan automatically get converted in the proper value depending on the
# column type.
df2.ix[["a", "c", "h"], ["one", "timestamp"]] = np.nan
display_df(df2)
print(df2.dtypes)
/Users/gp/.conda/envs/jupyter/lib/python2.7/site-packages/ipykernel_launcher.py:3: DeprecationWarning: 
.ix is deprecated. Please use
.loc for label based indexing or
.iloc for positional indexing

See the documentation here:
http://pandas.pydata.org/pandas-docs/stable/indexing.html#ix-indexer-is-deprecated
  This is separate from the ipykernel package so we can avoid doing imports until
Loading...
one                 float64
two                 float64
three               float64
four                 object
five                 object
timestamp    datetime64[ns]
dtype: object
print("pd.NaT == np.nan:", pd.NaT == np.nan)
print("pd.isnull(pd.NaT):", pd.isnull(pd.NaT))
pd.NaT == np.nan: False
pd.isnull(pd.NaT): True
print(df2.dtypes)

print(df2.get_dtype_counts())
one                 float64
two                 float64
three               float64
four                 object
five                 object
timestamp    datetime64[ns]
dtype: object
datetime64[ns]    1
float64           3
object            2
dtype: int64

Inserting missing data

s = pd.Series([1, 2, 3])
print(s)

# Since it is a container for floats None -> nan.
s.loc[0] = None
print(s)
0    1
1    2
2    3
dtype: int64
0    NaN
1    2.0
2    3.0
dtype: float64
s = pd.Series([1, 2, 3])
print(s)
# writing a char forces a type conversion of the container
s.loc[0] = "a"
print(s)
0    1
1    2
2    3
dtype: int64
0    a
1    2
2    3
dtype: object
s = pd.Series([1, "a", 3])
s.loc[0] = None
s.loc[2] = np.nan
print(s)
0    None
1       a
2     NaN
dtype: object
np.random.seed(1000)
df = pd.DataFrame(np.random.rand(5, 3))
df
Loading...
df.loc[0:1, 1:2]
Loading...
df.loc[[0, 1], [0, 2]]
Loading...
df.loc[[0, 2], [0, 2]] = np.nan
df
Loading...
df[0] + df[1]
0 NaN 1 1.354666 2 NaN 3 1.048823 4 0.574411 dtype: float64
# cumsum / cumprod data handles nans as zero for the running sum, but
# propagate nans when are there.
print(df[0][::-1].cumsum())
print(df[0].cumsum())
4    0.392154
3    1.233895
2         NaN
1    1.716086
0         NaN
Name: 0, dtype: float64
0         NaN
1    0.482191
2         NaN
3    1.323932
4    1.716086
Name: 0, dtype: float64
df.cumsum()
Loading...
# nan groups are excluded when grouping.
df.groupby(0).mean()
Loading...

Cleaning nans

df2
Loading...
# Note that 0 for timestamp is in epochs.
df2.fillna(0)
Loading...
df2.fillna("** MISSING **")
Loading...
df.loc[3, 0] = np.nan
df
Loading...
# Fill forward.
df.fillna(method="pad")
Loading...
# Limit number of fills.
df.fillna(method="pad", limit=1)
Loading...
list("ABC")
['A', 'B', 'C']
np.random.seed(10)
df = pd.DataFrame(np.random.randn(10, 3), columns=list("ABC"))
df
Loading...
df.iloc[3:5, 0] = np.nan
df.iloc[4:6, 1] = np.nan
df.iloc[5:8, 2] = np.nan
df
Loading...
df.sum()
A 4.296704 B 2.465829 C -0.117174 dtype: float64
# Count the non-null elements.
df.notnull().sum()
A 8 B 8 C 7 dtype: int64
# Note that mean counts nans as elements at the denominator.
df.mean()
A 0.537088 B 0.308229 C -0.016739 dtype: float64
# Skip nans in the count at the denominator.
df.mean() / df.notnull().sum()
A 0.067136 B 0.038529 C -0.002391 dtype: float64
# Count all elements.
df.mean() / df.shape[0]
A 0.053709 B 0.030823 C -0.001674 dtype: float64
df
Loading...
# DataFrame.where() is an extension of np.where.
# if pd.notnull(elem) returns 'elem' otherwise 'other'.
df.where(pd.notnull(df), other="missing")
Loading...
# Non-numeric values are still counted at the denominator.
df.mean()
A 0.537088 B 0.308229 C -0.016739 dtype: float64
# Fill with function of a column, similar to imputation.
df.where(pd.notnull(df), df.mean(), axis=1)
Loading...

Dropping axis with missing data.

df
Loading...
# Drop rows with any nan.
df.dropna(how="any")
Loading...
# Drop rows with all nans.
df.dropna(how="all")
Loading...
# Drop along columns.
df.dropna(how="any", axis="columns")
Loading...
np.random.seed(1000)
idx = pd.date_range("2000-01-01", periods=30)
ts = pd.Series(np.random.rand(len(idx)) * 2 - 1.0, index=idx)
ts.cumsum().plot(label="without nans")

ts.iloc[10:20] = np.nan
ts.fillna(0).cumsum().plot(label="with nans")
plt.legend(loc="best")
<matplotlib.figure.Figure at 0x1144e4f10>
ts.count()
20
ts.notnull().sum()
20
ts.cumsum().fillna(method="ffill").plot()
<matplotlib.figure.Figure at 0x1144e4250>
ts.cumsum().fillna(method="ffill", limit=3).plot()
<matplotlib.figure.Figure at 0x114524210>
# Anti-causal fill!
ts.cumsum().fillna(method="bfill", limit=3).plot()
<matplotlib.figure.Figure at 0x1144e4050>
# Before cumsum.
ts.plot()
<matplotlib.figure.Figure at 0x115906590>
ts.interpolate(method="linear").plot()
<matplotlib.figure.Figure at 0x115c34210>
np.random.seed(2)
ser = pd.Series(np.arange(1, 10.1, 0.25) ** 2 + np.random.randn(37))
bad = np.array([4, 13, 14, 15, 16, 17, 18, 20, 29])
ser[bad] = np.nan
ser.plot()
<matplotlib.figure.Figure at 0x115c98690>