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 inlineimport 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(),
)
dfLoading...
df.xs("a", axis=0)A 1
B 2
C 0
Name: a, dtype: int64df.xs("A", axis=1)a 1
b 3
c 5
Name: A, dtype: int64df.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: int64df["A"]a 1
b 3
c 5
Name: A, dtype: int64df.iloc[0]A 1
B 2
C 0
Name: a, dtype: int64Selecting 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()
dfLoading...
# 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
dfLoading...
# Select a piece of df and overwrite it.
df.ix[df.AAA >= 5, ["BBB", "CCC"]] = 555
dfLoading...
# Selecting a df through a mask.
df = df_orig.copy()
mask = pd.DataFrame(
{"AAA": [True] * 4, "BBB": [False] * 4, "CCC": [True, False] * 2}
)
maskLoading...
# 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: int64df.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
)
dfLoading...
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()
dfLoading...
# 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]}
)
dfLoading...
# 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: int64df.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.indexLoading...
# 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)
df4Loading...
df5 = df3.reset_index(0)
df5columns= 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"])
dfLoading...
# 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
)
dfLoading...
# 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"])
dfLoading...
# 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)
dfLoading...
# 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¶
dfLoading...
# 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),
}
)
dfLoading...
# 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"
dfLoading...
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"))
dfLoading...
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"
dfLoading...
# Add column function of another column.
df["five"] = df["one"] > 0
dfLoading...
# Add more indices by re-indexing.
df2 = df.reindex("a b c d e f g h".split())
df2Loading...
df2["one"].isnull()a False
b True
c False
d True
e False
f False
g True
h False
Name: one, dtype: booldf2["one"].notnull()a True
b False
c True
d False
e True
f True
g False
h True
Name: one, dtype: booldf2.emptyFalsedf2.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.nana False
b False
c False
d False
e False
f False
g False
h False
Name: one, dtype: boolDatetimes¶
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))
dfLoading...
df.loc[0:1, 1:2]Loading...
df.loc[[0, 1], [0, 2]]Loading...
df.loc[[0, 2], [0, 2]] = np.nan
dfLoading...
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¶
df2Loading...
# Note that 0 for timestamp is in epochs.
df2.fillna(0)Loading...
df2.fillna("** MISSING **")Loading...
df.loc[3, 0] = np.nan
dfLoading...
# 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"))
dfLoading...
df.iloc[3:5, 0] = np.nan
df.iloc[4:6, 1] = np.nan
df.iloc[5:8, 2] = np.nan
dfLoading...
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: float64dfLoading...
# 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.¶
dfLoading...
# 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")
ts.count()20ts.notnull().sum()20ts.cumsum().fillna(method="ffill").plot()
ts.cumsum().fillna(method="ffill", limit=3).plot()
# Anti-causal fill!
ts.cumsum().fillna(method="bfill", limit=3).plot()
# Before cumsum.
ts.plot()
ts.interpolate(method="linear").plot()
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()