时间序列#

import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import polars as pl
from dateutil.parser import parse

Peaks#

passengers = pl.read_csv("data/air_passengers.csv").with_row_index()
passengers.head()
shape: (5, 3)
indexdatevalue
u32stri64
0"1949-01-01"112
1"1949-02-01"118
2"1949-03-01"132
3"1949-04-01"129
4"1949-05-01"121
traffic = passengers["value"]
doubled_iff = np.diff(np.sign(np.diff(traffic)))
peak_locations = np.where(doubled_iff == -2)[0] + 1

doubled_iff2 = np.diff(np.sign(np.diff(-1 * traffic)))
trough_locations = np.where(doubled_iff2 == -2)[0] + 1
_, ax = plt.subplots(figsize=(10, 6))

ax.plot("date", "value", data=passengers, color="tab:blue", label="Air Traffic")

ax.scatter(
    passengers["date"][peak_locations],
    passengers["value"][peak_locations],
    marker=mpl.markers.CARETUPBASE,
    color="tab:green",
    s=100,
    label="Peaks",
)

ax.scatter(
    passengers["date"][trough_locations],
    passengers["value"][trough_locations],
    marker=mpl.markers.CARETDOWNBASE,
    color="tab:red",
    s=100,
    label="Troughs",
)

for t, p in zip(trough_locations.tolist()[1::5], peak_locations.tolist()[::3]):
    ax.text(
        passengers["date"][p],
        passengers["value"][p] + 15,
        passengers["date"][p],
        horizontalalignment="center",
        color="darkgreen",
    )

    ax.text(
        passengers["date"][t],
        passengers["value"][t] - 35,
        passengers["date"][t],
        horizontalalignment="center",
        color="darkred",
    )

xtick_location = passengers["index"].to_list()[::6]
xtick_labels = passengers["date"].to_list()[::6]
ytick_labels = passengers["value"].to_list()[::6]

ax.set_xticks(ticks=xtick_location)
ax.set_xticklabels(labels=xtick_labels, rotation=90, alpha=0.7)
ax.set(ylim=(50, 750), title="Peak and Troughs of Air Passengers Traffic (1949 - 1969)")

ax.spines[["top", "right"]].set_alpha(0.0)
ax.spines[["bottom", "left"]].set_alpha(0.3)
ax.grid(axis="y", alpha=0.3)
ax.legend(loc="upper left")
<matplotlib.legend.Legend at 0x7fc464e52120>
../_images/c98920ee0c1aeb953c9a1cc796ca326500ff084da21ebb9bf074c646098db08b.png

Seasonal#

passengers = passengers.with_columns(
    year=pl.col("date").map_elements(lambda d: parse(d).year, return_dtype=pl.Int64),
    month=pl.col("date").map_elements(
        lambda d: parse(d).strftime("%b"), return_dtype=pl.String
    ),
)
passengers
shape: (144, 5)
indexdatevalueyearmonth
u32stri64i64str
0"1949-01-01"1121949"Jan"
1"1949-02-01"1181949"Feb"
2"1949-03-01"1321949"Mar"
3"1949-04-01"1291949"Apr"
4"1949-05-01"1211949"May"
139"1960-08-01"6061960"Aug"
140"1960-09-01"5081960"Sep"
141"1960-10-01"4611960"Oct"
142"1960-11-01"3901960"Nov"
143"1960-12-01"4321960"Dec"
years = passengers["year"].unique()

mycolors = [
    "tab:red",
    "tab:blue",
    "tab:green",
    "tab:orange",
    "tab:brown",
    "tab:grey",
    "tab:pink",
    "tab:olive",
    "deeppink",
    "steelblue",
    "firebrick",
    "mediumseagreen",
]

_, ax = plt.subplots(figsize=(10, 6))

for i, y in enumerate(years):
    pass_year = passengers.filter(pl.col("year") == y)
    ax.plot("month", "value", data=pass_year, color=mycolors[i], label=y)
    ax.text(
        pass_year.shape[0] - 0.9,
        pass_year["value"][-1:].to_numpy()[0],
        y,
        fontsize="small",
        color=mycolors[i],
    )

ax.set(
    xlim=(-0.3, 11),
    ylim=(50, 750),
    ylabel="$Air Traffic$",
    title="Monthly Seasonal Plot: Air Passengers Traffic (1949 - 1969)",
)
ax.grid(axis="y", alpha=0.3)
../_images/5d62100b3286b33211d490b1ce97dcb6d4086188f5f42e273fba58322e93ab9e.png

Cross Correlation#

from statsmodels.tsa import stattools
mortality = pl.read_csv("data/mortality.csv")
mortality.head()
shape: (5, 3)
datemdeathsfdeaths
stri64i64
"Jan 1974"2134901
"Feb 1974"1863689
"Mar 1974"1877827
"Apr 1974"1877677
"May 1974"1492522
x = mortality["mdeaths"]
y = mortality["fdeaths"]

ccs = stattools.ccf(x, y)[:100]
nlags = len(ccs)

conf_level = 2 / np.sqrt(nlags)

_, ax = plt.subplots(figsize=(10, 6))

ax.hlines(0, xmin=0, xmax=100, color="gray")
ax.hlines(conf_level, xmin=0, xmax=100, color="gray")
ax.hlines(-conf_level, xmin=0, xmax=100, color="gray")

ax.bar(x=np.arange(len(ccs)), height=ccs, width=0.3)
ax.set(xlim=(0, len(ccs)), title=r"$Cross\ Correlation\ Plot:\ mdeaths\ vs\ fdeaths$")
[(0.0, 72.0),
 Text(0.5, 1.0, '$Cross\\ Correlation\\ Plot:\\ mdeaths\\ vs\\ fdeaths$')]
../_images/66133e5b5f5bec1364030747ac13a19c03a72d2bbb3973fa172486e2fc755225.png

Decomposition#

from statsmodels.tsa.seasonal import seasonal_decompose
import pandas as pd

passengers = pd.read_csv("data/air_passengers.csv")
passengers.head()
date value
0 1949-01-01 112
1 1949-02-01 118
2 1949-03-01 132
3 1949-04-01 129
4 1949-05-01 121
date = passengers["date"]
dates = pd.DatetimeIndex(data=date)

passengers = passengers.set_index(dates)
result = seasonal_decompose(passengers["value"], model="multiplicative")
result = seasonal_decompose(passengers["value"], model="multiplicative")
fig, axes = plt.subplots(4, 1, figsize=(10, 6), sharex=True, constrained_layout=True)

ys = (result.observed, result.trend, result.seasonal, result.resid)
ylabels = ("Value", "Trend", "Seasonal", "Residual")

for ax, y, ylabel in zip(axes.flatten(), ys, ylabels):
    ax.plot(dates, y)
    ax.set(ylabel=ylabel)

fig.suptitle("Time Series Decomposition of Air Passengers")
Text(0.5, 0.98, 'Time Series Decomposition of Air Passengers')
../_images/833936c83d6f7aad04c379d36d21ad111224cddb868fa8a7504c9820c997d3ed.png

Double Y#

economics = pd.read_csv("data/economics.csv", parse_dates=["date"])
economics.head()
date pce pop psavert uempmed unemploy
0 1967-07-01 507.4 198712 12.5 4.5 2944
1 1967-08-01 510.5 198911 12.5 4.7 2945
2 1967-09-01 516.3 199113 11.7 4.6 2958
3 1967-10-01 512.9 199311 12.5 4.9 3143
4 1967-11-01 518.1 199498 12.5 4.7 3066
x = economics["date"]
y1 = economics["psavert"]
y2 = economics["unemploy"]

_, ax = plt.subplots(figsize=(10, 6))
ax.plot(x, y1, color="tab:red")

# Plot Line2 (Right Y Axis)
ax2 = ax.twinx()  # instantiate a second axes that shares the same x-axis
ax2.plot(x, y2, color="tab:blue")

ax.tick_params(axis="x", rotation=0)
ax.set(xlabel="Year", ylabel="Personal Savings Rate")
ax.tick_params(axis="y", rotation=0, labelcolor="tab:red")
ax.grid(alpha=0.4)

ax2.tick_params(axis="y", labelcolor="tab:blue")
ax2.set(
    ylabel="# Unemployed (1000's)",
    title="Personal Savings Rate vs Unemployed: Plotting in Secondary Y Axis",
)
[Text(0, 0.5, "# Unemployed (1000's)"),
 Text(0.5, 1.0, 'Personal Savings Rate vs Unemployed: Plotting in Secondary Y Axis')]
../_images/586e2e3a5a33949f934f4cb7025304f6de47b274389294018c7d2335c4d3bea1.png

SEM#

from scipy.stats import sem
orders = pd.read_csv("data/user_orders_hourofday.csv")
orders.head()
user_id order_hour_of_day quantity
0 1 7 20
1 1 8 23
2 1 9 12
3 1 12 11
4 1 14 10
orders_mean = orders.groupby("order_hour_of_day").quantity.mean()
orders_se = orders.groupby("order_hour_of_day").quantity.apply(sem).mul(1.96)

x = orders_mean.index

fig, ax = plt.subplots(figsize=(10, 6))
ax.set(ylabel="# Orders")
ax.plot(x, orders_mean, color="white", lw=2)
ax.fill_between(x, orders_mean - orders_se, orders_mean + orders_se, color="#3F5D7D")

s, e = ax.get_xlim()
ax.set(
    xlim=(s, e),
    xlabel="Hour of Day",
    title="User Orders by Hour of Day (95% confidence)",
)

for y in range(8, 20, 2):
    ax.hlines(y, xmin=s, xmax=e, colors="black", alpha=0.5, linestyles="--", lw=0.5)

ax.spines[["top", "right"]].set_alpha(0)
ax.spines[["bottom", "left"]].set_alpha(1)
../_images/8fbf15ccbdd8e56da994a4b5c9dc9c4e6aa2f0caf94069ae686422098f401aa7.png
from scipy.stats import sem
orders_45d = pd.read_csv(
    "data/orders_45d.csv", parse_dates=["purchase_time", "purchase_date"]
)
orders_45d.head()
purchase_time purchase_date quantity
0 2017-05-16 13:10:30 2017-05-16 5
1 2017-05-16 19:41:10 2017-05-16 3
2 2017-05-19 18:53:40 2017-05-19 2
3 2017-05-18 13:55:47 2017-05-18 1
4 2017-05-14 20:28:25 2017-05-14 3
orders_mean = orders_45d.groupby("purchase_date").quantity.mean()
orders_se = orders_45d.groupby("purchase_date").quantity.apply(sem).mul(1.96)

x = [d.date().strftime("%Y-%m-%d") for d in orders_mean.index]

_, ax = plt.subplots(figsize=(10, 6))

ax.plot(x, orders_mean, color="white", lw=2)
ax.fill_between(x, orders_mean - orders_se, orders_mean + orders_se, color="#3F5D7D")

s, e = ax.get_xlim()
ax.set(
    xticks=x[::5],
    xlim=(s, e - 2),
    ylim=(4, 10),
    ylabel="# Daily Orders",
    title="Daily Order Quantity of Brazilian Retail with Error Bands (95% confidence)",
)

for y in range(5, 10):
    ax.hlines(y, xmin=s, xmax=e, colors="black", alpha=0.5, linestyles="--", lw=0.5)

ax.set_xticklabels(
    x[::5],
    rotation=60,
    fontdict={"horizontalalignment": "center", "verticalalignment": "center_baseline"},
)
ax.spines[["top", "right"]].set_alpha(0)
ax.spines[["bottom", "left"]].set_alpha(1)
../_images/9d80e192cfc07494346fe4ea41db5a1a13d5236899b944fb75c5165ab69dff67.png

Area#

economics = pd.read_csv("data/economics.csv", parse_dates=["date"])

x = np.arange(economics.shape[0])
y_returns = (economics.psavert.diff().fillna(0) / economics.psavert.shift(1)).fillna(
    0
) * 100

_, ax = plt.subplots(figsize=(10, 6))

ax.fill_between(
    x[1:],
    y_returns[1:],
    0,
    where=y_returns[1:] >= 0,
    facecolor="green",
    interpolate=True,
    alpha=0.7,
)

ax.fill_between(
    x[1:],
    y_returns[1:],
    0,
    where=y_returns[1:] <= 0,
    facecolor="red",
    interpolate=True,
    alpha=0.7,
)

ax.annotate(
    "Peak \n1975",
    xy=(94.0, 21.0),
    xytext=(88.0, 28),
    bbox={"boxstyle": "square", "fc": "firebrick"},
    arrowprops={"facecolor": "steelblue", "shrink": 0.05},
    fontsize="medium",
    color="white",
)

ax.set(
    xticks=x[::6],
    xlim=(1, 100),
    ylim=(-35, 35),
    title="Month Economics Return %",
    ylabel="Monthly returns %",
)

xtickvals = [
    f"{str(m)[:3].upper()}-{y!s}"
    for y, m in zip(economics["date"].dt.year, economics["date"].dt.month_name())
]
ax.set_xticklabels(
    xtickvals[::6],
    rotation=60,
    fontdict={"horizontalalignment": "center", "verticalalignment": "center_baseline"},
)
ax.grid(alpha=0.5)
../_images/441e0be4b34125549bd56bf43d7f843031df82e56469699bc1a897b4799881da.png

Stacked Area#

visitors = pd.read_csv(
    "data/night_visitors.csv", parse_dates=["yearmon"], date_format="%Y-%m-%d"
)
visitors.head()
yearmon Sydney NSW Melbourne VIC BrisbaneGC QLD Capitals Other
0 Jan 1998 7320 21782 4865 14054 9055 8016 9178 10232
1 Apr 1998 6117 16881 4100 8237 5616 8461 6362 9540
2 Jul 1998 6282 13495 4418 6731 8298 13175 7965 12385
3 Oct 1998 6368 15963 5157 7675 6674 9092 6864 13098
4 Jan 1999 6602 22718 5550 13581 9168 10224 8908 10140
mycolors = [
    "tab:red",
    "tab:blue",
    "tab:green",
    "tab:orange",
    "tab:brown",
    "tab:grey",
    "tab:pink",
    "tab:olive",
]

columns = visitors.columns[1:]
labs = columns.to_numpy().tolist()

x = visitors["yearmon"].to_numpy().tolist()

ys = []

for i in range(8):
    yi = visitors[columns[i]].to_numpy().tolist()
    ys.append(yi)

y = np.vstack(ys)

labs = columns.to_numpy().tolist()

_, ax = plt.subplots(figsize=(10, 6))

ax.stackplot(x, y, labels=labs, colors=mycolors, alpha=0.8)

ax.set(
    xticks=x[::5],
    xlim=(x[0], x[-1]),
    ylim=[0, 100000],
    title="Night Visitors in Australian Regions",
)
ax.spines[["right", "top", "left", "bottom"]].set_visible(False)
plt.setp(
    ax.get_xticklabels(),
    rotation=60,
    horizontalalignment="right",
    verticalalignment="top",
)
ax.legend(ncol=4)
<matplotlib.legend.Legend at 0x7fc419022e90>
../_images/aae5ae6dd380792041e64a66263f1451147151186627ff94010ac450e1fcfeb4.png

Unstacked Area#

x = economics["date"].array.tolist()
y1 = economics["psavert"].array.tolist()
y2 = economics["uempmed"].array.tolist()
mycolors = [
    "tab:red",
    "tab:blue",
    "tab:green",
    "tab:orange",
    "tab:brown",
    "tab:grey",
    "tab:pink",
    "tab:olive",
]
columns = ["psavert", "uempmed"]

fig, ax = plt.subplots(1, 1, figsize=(14, 8))
ax.fill_between(x, y1=y1, y2=0, label=columns[1], alpha=0.5, color=mycolors[1], lw=2)
ax.fill_between(x, y1=y2, y2=0, label=columns[0], alpha=0.5, color=mycolors[0], lw=2)

ax.set(xlim=(-10, x[-1]), ylim=[0, 30])
ax.set_title(
    "Personal Savings Rate vs Median Duration of Unemployment", fontsize="xx-large"
)
ax.legend(loc="best")
plt.setp(ax.get_xticklabels()[::50], horizontalalignment="center")
plt.setp(ax.get_yticklabels()[::50], horizontalalignment="right")

for y in np.arange(2.5, 30.0, 2.5):
    ax.hlines(
        y, xmin=0, xmax=len(x), colors="black", alpha=0.3, linestyles="--", lw=0.5
    )
ax.spines[["right", "top", "left", "bottom"]].set_visible(False)
../_images/42e5b571b8993f5fddb85d7b441423a7020490b07a7304f53471953c6b09b0d2.png