This is a personal project to get insights related to my personal expenses. In this notebook I analyze bank statements from my everyday banking account and my two credit cards using Topic Modelling with the idea to classify transactions. I also take a look on how my transactions have been affected by COVID 19.
In this notebook I am practicing my skills in:
I spent 172.89 CAD in coffee since Dec. 1, 2018. I love coffee!

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from plotly.subplots import make_subplots
import plotly.graph_objects as go
import plotly.offline as px
import datetime
import re # Regular expressions
import nltk # natural language tool kit
from sklearn.model_selection import GridSearchCV
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.decomposition import LatentDirichletAllocation
import warnings
warnings.filterwarnings('ignore')
%matplotlib inline
print("Pandas version: ", pd.__version__)
# To export plotly figures in an HTML version of the jupyter notebook
px.init_notebook_mode()
# Read the dataset for Banking account
df_banking = pd.read_csv('banking.csv', header=None, usecols=[0, 1, 3, 4], names = ['Transaction Date', 'Quantity', 'Transaction description', 'Transaction Type'], parse_dates=['Transaction Date'])
df_banking = df_banking.set_index('Transaction Date')
print(f'Shape of dataframe: {df_banking.shape}')
df_banking = df_banking.sort_index()
df_banking.head()
# Read the dataset for credit card bank 1
df_creditcard_bank01 = pd.read_csv('bank01_creditcard.csv', header=None, names = ['Transaction Date', 'Transaction description', 'Quantity'], parse_dates=['Transaction Date'])
df_creditcard_bank01 = df_creditcard_bank01.set_index('Transaction Date')
print(f'Shape of dataframe: {df_creditcard_bank01.shape}')
df_creditcard_bank01 = df_creditcard_bank01.sort_index()
df_creditcard_bank01.head()
# Read the dataset for credit card bank 2 and join them (In this case the transactions available are only last 25 months and you can download 12 months at a time)
df_creditcard_bank02a = pd.read_csv('bank02_creditcard_01.csv', usecols=[0, 3, 4, 5, 6], parse_dates=['Transaction Date'])
df_creditcard_bank02b = pd.read_csv('bank02_creditcard_02.csv', usecols=[0, 3, 4, 5, 6], parse_dates=['Transaction Date'])
df_creditcard_bank02 = pd.concat([df_creditcard_bank02a, df_creditcard_bank02b ])
df_creditcard_bank02 = df_creditcard_bank02.set_index('Transaction Date')
# Debit column should be negative
df_creditcard_bank02['Debit'] = -df_creditcard_bank02['Debit']
# Null values should be zero in Debit and credit columns. A new column 'Quantity' is created from Debit and Credit
df_creditcard_bank02.loc[df_creditcard_bank02['Debit'].isnull(), 'Debit'] = 0
df_creditcard_bank02.loc[df_creditcard_bank02['Credit'].isnull(), 'Credit'] = 0
df_creditcard_bank02['Quantity'] = df_creditcard_bank02['Debit'] + df_creditcard_bank02['Credit']
print(f'Shape of dataframe: {df_creditcard_bank02.shape}')
# df_creditcard_bank02 = df_creditcard_bank02.rename(columns={'Debit': 'Quantity'})
df_creditcard_bank02 = df_creditcard_bank02.sort_index()
df_creditcard_bank02.head()
df_banking.info()
# Fill NULL transaction type with "Transaction description"
df_banking['Transaction Type'] = df_banking['Transaction Type'].fillna(df_banking['Transaction description'])
df_creditcard_bank01.info()
df_creditcard_bank02.info()
df_banking['Year'] = df_banking.index.year
df_banking['Month'] = df_banking.index.month
df_banking['Day'] = df_banking.index.day
df_banking['Week'] = df_banking.index.isocalendar().week
df_banking['Weekday'] = df_banking.index.weekday
df_creditcard_bank01['Year'] = df_creditcard_bank01.index.year
df_creditcard_bank01['Month'] = df_creditcard_bank01.index.month
df_creditcard_bank01['Day'] = df_creditcard_bank01.index.day
df_creditcard_bank01['Week'] = df_creditcard_bank01.index.isocalendar().week
df_creditcard_bank01['Weekday'] = df_creditcard_bank01.index.weekday
df_creditcard_bank02['Year'] = df_creditcard_bank02.index.year
df_creditcard_bank02['Month'] = df_creditcard_bank02.index.month
df_creditcard_bank02['Day'] = df_creditcard_bank02.index.day
df_creditcard_bank02['Week'] = df_creditcard_bank02.index.isocalendar().week
df_creditcard_bank02['Weekday'] = df_creditcard_bank02.index.weekday
Getting to know the dataset is very important. As initial steps I looked for the maximum and minimum transactions of each financial product. Then I looked for the available dates and general distribution of the transactions. I also checked how the transaction are distributed in time, month and day of the week. Finally, I compared IN/OUT transactions in 2019 period with the 2020 period as Sep. 30.
min_Banking = df_banking["Quantity"].min()
min_Banking_date = df_banking["Quantity"].idxmin()
max_Banking = df_banking["Quantity"].max()
max_Banking_date = df_banking["Quantity"].idxmax()
print(f"The minimum value transaction for banking account was: {min_Banking} CAD on {min_Banking_date.date()}")
print(f"The maximum value transaction for banking account was: {max_Banking} CAD on {max_Banking_date.date()}")
min_Credit_bank01 = df_creditcard_bank01["Quantity"].min()
min_Credit_bank01_date = df_creditcard_bank01["Quantity"].idxmin()
max_Credit_bank01 = df_creditcard_bank01["Quantity"].max()
max_Credit_bank01_date = df_creditcard_bank01["Quantity"].idxmax()
print(f"The minimum value transaction for banking account was: {min_Credit_bank01} CAD on {min_Credit_bank01_date.date()}")
print(f"The maximum value transaction for banking account was: {max_Credit_bank01} CAD on {max_Credit_bank01_date.date()}")
min_Credit_bank02 = df_creditcard_bank02["Quantity"].min()
min_Credit_bank02_date = df_creditcard_bank02["Quantity"].idxmin()
max_Credit_bank02 = df_creditcard_bank02["Quantity"].max()
max_Credit_bank02_date = df_creditcard_bank02["Quantity"].idxmax()
print(f"The minimum value transaction for banking account was: {min_Credit_bank02} CAD on {min_Credit_bank02_date.date()}")
print(f"The maximum value transaction for banking account was: {max_Credit_bank02} CAD on {max_Credit_bank02_date.date()}")
print(f"First available date of banking data is {df_banking.index[0].date()}")
print(f"First available date of credit card bank 1 data is {df_creditcard_bank01.index[0].date()}")
print(f"First available date of credit card bank 2 data is {df_creditcard_bank02.index[0].date()}")
fig = make_subplots(rows=1, cols=3, subplot_titles=("Banking", "Credit Card 1", "Credit Card 2"))
fig.add_trace(
go.Histogram(x=df_banking["Quantity"].values, name="Banking"),
row=1, col=1
)
fig.add_trace(
go.Histogram(x=df_creditcard_bank01["Quantity"].values, name="Credit Card 1"),
row=1, col=2
)
fig.add_trace(
go.Histogram(x=df_creditcard_bank02["Quantity"].values, name="Credit Card 2"),
row=1, col=3
)
fig.update_layout(height=400, width=800, title_text="Histograms for different financial products")
fig.update_xaxes(title_text="Quantity", row=1, col=1)
fig.update_xaxes(title_text="Quantity", row=1, col=2)
fig.update_xaxes(title_text="Quantity", row=1, col=3)
fig.update_yaxes(title_text="Count", row=1, col=1)
fig.update_traces(marker_line_width=1.5, opacity=0.6)
fig.show()
# Create figure and plot space
fig, axes = plt.subplots(3, 1, figsize=(12, 15))
fig.subplots_adjust(hspace=0.5)
# For banking
axes[0].bar(df_banking.index.values, df_banking['Quantity'])
axes[1].bar(df_creditcard_bank01.index.values, df_creditcard_bank01['Quantity'])
axes[2].bar(df_creditcard_bank02.index.values, df_creditcard_bank02['Quantity'])
# Set title and labels for axes
axes[0].set(xlabel="Date", ylabel="Quantity (CAD)", title="Banking transactions")
axes[1].set(xlabel="Date", ylabel="Quantity (CAD)", title="Credit card transactions from bank 1")
axes[2].set(xlabel="Date", ylabel="Quantity (CAD)", title="Credit card transactions from bank 2")
I am interested to see if the month, the day of the week, the day of month or the week of the year will make a difference
fig = make_subplots(rows=3, cols=1, subplot_titles=("Banking", "Credit Card 1", "Credit Card 2"))
fig.add_trace(
go.Histogram(x=df_banking["Month"].values, name="Banking"),
row=1, col=1
)
fig.add_trace(
go.Histogram(x=df_creditcard_bank01["Month"].values, name="Credit Card 1"),
row=2, col=1
)
fig.add_trace(
go.Histogram(x=df_creditcard_bank02["Month"].values, name="Credit Card 2"),
row=3, col=1
)
fig.update_layout(height=800, width=800, title_text="Plot for different financial products in time")
fig.update_xaxes(title_text="Month", row=3, col=1)
fig.update_yaxes(title_text="Count", row=1, col=1)
fig.update_yaxes(title_text="Count", row=2, col=1)
fig.update_yaxes(title_text="Count", row=3, col=1)
fig.update_traces(marker_line_width=1.5, opacity=0.6)
fig.show()
fig = make_subplots(rows=3, cols=1, subplot_titles=("Banking", "Credit Card 1", "Credit Card 2"))
fig.add_trace(
go.Histogram(x=df_banking["Day"].values, name="Banking"),
row=1, col=1
)
fig.add_trace(
go.Histogram(x=df_creditcard_bank01["Day"].values, name="Credit Card 1"),
row=2, col=1
)
fig.add_trace(
go.Histogram(x=df_creditcard_bank02["Day"].values, name="Credit Card 2"),
row=3, col=1
)
fig.update_layout(height=800, width=800, title_text="Plot for different financial products in time")
fig.update_xaxes(title_text="Day", row=3, col=1)
fig.update_yaxes(title_text="Count", row=1, col=1)
fig.update_yaxes(title_text="Count", row=2, col=1)
fig.update_yaxes(title_text="Count", row=3, col=1)
fig.update_traces(marker_line_width=1.5, opacity=0.6)
fig.show()
fig = make_subplots(rows=3, cols=1, subplot_titles=("Banking", "Credit Card 1", "Credit Card 2"))
fig.add_trace(
go.Histogram(x=df_banking["Week"].values, name="Banking"),
row=1, col=1
)
fig.add_trace(
go.Histogram(x=df_creditcard_bank01["Week"].values, name="Credit Card 1"),
row=2, col=1
)
fig.add_trace(
go.Histogram(x=df_creditcard_bank02["Week"].values, name="Credit Card 2"),
row=3, col=1
)
fig.update_layout(height=800, width=800, title_text="Plot for different financial products in time")
fig.update_xaxes(title_text="Week", row=3, col=1)
fig.update_yaxes(title_text="Count", row=1, col=1)
fig.update_yaxes(title_text="Count", row=2, col=1)
fig.update_yaxes(title_text="Count", row=3, col=1)
fig.update_traces(marker_line_width=1.5, opacity=0.6)
fig.show()
fig = make_subplots(rows=3, cols=1, subplot_titles=("Banking", "Credit Card 1", "Credit Card 2"))
fig.add_trace(
go.Histogram(x=df_banking["Weekday"].values, name="Banking"),
row=1, col=1
)
fig.add_trace(
go.Histogram(x=df_creditcard_bank01["Weekday"].values, name="Credit Card 1"),
row=2, col=1
)
fig.add_trace(
go.Histogram(x=df_creditcard_bank02["Weekday"].values, name="Credit Card 2"),
row=3, col=1
)
fig.update_layout(height=800, width=800, title_text="Plot for different financial products in time")
fig.update_xaxes(title_text="Weekday", row=3, col=1)
fig.update_yaxes(title_text="Count", row=1, col=1)
fig.update_yaxes(title_text="Count", row=2, col=1)
fig.update_yaxes(title_text="Count", row=3, col=1)
fig.update_traces(marker_line_width=1.5, opacity=0.6)
fig.show()
df_banking.head()
# Transactions in 2019
df_2019_credit02_IN = df_creditcard_bank01[ (df_creditcard_bank01['Year'] == 2019) & (df_creditcard_bank01['Quantity'] > 0 )]
df_2019_credit02_OUT = df_creditcard_bank01[ (df_creditcard_bank01['Year'] == 2019) & (df_creditcard_bank01['Quantity'] < 0 )]
df_2019_credit02_IN
data = [
[2019, df_banking[ (df_banking['Year'] == 2019) & (df_banking['Quantity'] > 0 )]['Quantity'].sum(), -df_banking[ (df_banking['Year'] == 2019) & (df_banking['Quantity'] < 0 )]['Quantity'].sum(), 'Banking'],
[2019, df_creditcard_bank01[ (df_creditcard_bank01['Year'] == 2019) & (df_creditcard_bank01['Quantity'] > 0 )]['Quantity'].sum(), -df_creditcard_bank01[ (df_creditcard_bank01['Year'] == 2019) & (df_creditcard_bank01['Quantity'] < 0 )]['Quantity'].sum(), 'Credit Card Bank 1'],
[2019, df_creditcard_bank02[ (df_creditcard_bank02['Year'] == 2019) & (df_creditcard_bank02['Quantity'] > 0 )]['Quantity'].sum(), -df_creditcard_bank02[ (df_creditcard_bank02['Year'] == 2019) & (df_creditcard_bank02['Quantity'] < 0 )]['Quantity'].sum(), 'Credit Card Bank 2'],
[2020, df_banking[ (df_banking['Year'] == 2020) & (df_banking['Quantity'] > 0 )]['Quantity'].sum(), -df_banking[ (df_banking['Year'] == 2020) & (df_banking['Quantity'] < 0 )]['Quantity'].sum(), 'Banking'],
[2020, df_creditcard_bank01[ (df_creditcard_bank01['Year'] == 2020) & (df_creditcard_bank01['Quantity'] > 0 )]['Quantity'].sum(), -df_creditcard_bank01[ (df_creditcard_bank01['Year'] == 2020) & (df_creditcard_bank01['Quantity'] < 0 )]['Quantity'].sum(), 'Credit Card Bank 1'],
[2020, df_creditcard_bank02[ (df_creditcard_bank02['Year'] == 2020) & (df_creditcard_bank02['Quantity'] > 0 )]['Quantity'].sum(), -df_creditcard_bank02[ (df_creditcard_bank02['Year'] == 2020) & (df_creditcard_bank02['Quantity'] < 0 )]['Quantity'].sum(), 'Credit Card Bank 2']
]
df_transactionsSummary = pd.DataFrame(data = data, columns=['Year', 'IN', 'OUT', 'Origin'])
df_transactionsSummary['Difference'] = df_transactionsSummary['IN'] - df_transactionsSummary['OUT']
df_transactionsSummary
change_bank = df_transactionsSummary[df_transactionsSummary['Origin'] == "Banking"]['IN'].reset_index(drop=True).values
change_CC1 = df_transactionsSummary[df_transactionsSummary['Origin'] == "Credit Card Bank 1"]['OUT'].reset_index(drop=True).values
change_CC2 = df_transactionsSummary[df_transactionsSummary['Origin'] == "Credit Card Bank 2"]['OUT'].reset_index(drop=True).values
print('Debt (IN):', "{:.2f}".format(100*(change_bank[1] - change_bank[0])/change_bank[0]) + '%' )
print('Credit Card 1 (OUT):', "{:.2f}".format(100*(change_CC1[1] - change_CC1[0])/change_CC1[0]) + '%')
print('Credit Card 2 (OUT):', "{:.2f}".format(100*(change_CC2[1] - change_CC2[0])/change_CC2[0]) + '%')
df_transactionsSummary_banking = df_transactionsSummary[df_transactionsSummary['Origin'] == 'Banking']
import plotly.express as px
fig = px.bar(df_transactionsSummary_banking, x="Year", y=["IN", "OUT"],
labels={
"value": "Quantity (CAD)",
"variable": "Type"
},
barmode='group',
height=400, width=600)
fig.update_traces(marker_line_width=1.5, opacity=0.6)
fig.update_layout(title_text='Quantity of Debit Transactions - 2019 vs 2020')
fig.show()
df_transactionsSummary_CC1 = df_transactionsSummary[df_transactionsSummary['Origin'] == 'Credit Card Bank 1']
import plotly.express as px
fig = px.bar(df_transactionsSummary_CC1, x="Year", y=["IN", "OUT"],
labels={
"value": "Quantity (CAD)",
"variable": "Type"
},
barmode='group',
height=400, width=600)
fig.update_traces(marker_line_width=1.5, opacity=0.6)
fig.update_layout(title_text='Quantity of Credit Card Transactions (Bank 1) - 2019 vs 2020')
fig.show()
df_transactionsSummary_CC2 = df_transactionsSummary[df_transactionsSummary['Origin'] == 'Credit Card Bank 2']
fig = px.bar(df_transactionsSummary_CC2, x="Year", y=["IN", "OUT"],
labels={
"value": "Quantity (CAD)",
"variable": "Type"
},
barmode='group',
height=400, width=600)
fig.update_traces(marker_line_width=1.5, opacity=0.6)
fig.update_layout(title_text='Quantity of Credit Card Transactions (Bank 2) - 2019 vs 2020')
fig.show()
Comparing 2019 vs. 2020 (up to Sep. 30)
The idea of Topic Modelling in Natural Language Processing is to discover abstract topics in documents. The Latent Dirichlet Allocation (LDA) is a generative statistical model that allows sets of observations to be explained by unobserved groups. This is within the Unsupervised Learning category. Some examples can be found here: Example 1, Example 2, Example 3
Some preparation is needed first.
date01 = datetime.date(2018, 12, 1)
date02 = datetime.date(2020, 9, 30)
df_banking = df_banking[(df_banking.index.date >= date01) & (df_banking.index.date <= date02)]
df_creditcard_bank01 = df_creditcard_bank01[(df_creditcard_bank01.index.date >= date01) & (df_creditcard_bank01.index.date <= date02)]
df_creditcard_bank02 = df_creditcard_bank02[(df_creditcard_bank02.index.date >= date01) & (df_creditcard_bank02.index.date <= date02)]
A quick look on the description field show us that we can use it to help the classification of transactions. It is interesting that Bank 2 provides a category field. For this study that column won't be used.
df_banking['Transaction description'].unique()[:5]
df_banking['Transaction Type'].unique()[:5]
df_creditcard_bank01['Transaction description'].unique()[:5]
df_creditcard_bank02['Description'].unique()[:5]
# Category field for second credit card wont be used
categories_CC2 = df_creditcard_bank02['Category'].unique()
print(categories_CC2.shape)
categories_CC2
df_banking[['Transaction description', 'Transaction Type', 'Year', 'Quantity']].groupby(['Transaction description', 'Transaction Type', 'Year']).sum().head()
df_creditcard_bank01[['Transaction description', 'Year', 'Quantity']].groupby(['Transaction description', 'Year']).sum().head()
df_creditcard_bank02[['Description', 'Year', 'Quantity']].groupby(['Description', 'Year']).sum().head()
I will join the three data sets. A new column corresponding to the original dataset will be created
df_banking['Original DF'] = 'Banking'
df_creditcard_bank01['Original DF'] = 'Credit Card - Bank 1'
df_creditcard_bank02['Original DF'] = 'Credit Card - Bank 2'
df_transactions_01a = df_banking.copy()
df_transactions_01a = df_transactions_01a.drop(columns=['Transaction Type'])
df_transactions_01a = df_transactions_01a.reset_index()
df_transactions_01b = df_creditcard_bank01.copy()
df_transactions_01b = df_transactions_01b.reset_index()
df_transactions_01c = df_creditcard_bank02.copy()
df_transactions_01c = df_transactions_01c.drop(columns=['Category', 'Debit', 'Credit'])
df_transactions_01c = df_transactions_01c.rename(columns={'Description': 'Transaction description'})
df_transactions_01c = df_transactions_01c.reset_index()
df_transactions_01b
frames = [df_transactions_01a, df_transactions_01b, df_transactions_01c]
df_transactions = pd.concat(frames)
df_transactions = df_transactions.reset_index().drop(columns='index').sort_values(['Transaction Date']).reset_index().drop(columns='index')
df_transactions.head()
Within the transaction description, we can see items with numbers, for example for transactions related with Dollarama have associated a number corresponding to the store. However, for this analysis what I want to know is the category of the expense and not the place. In that sense, the 'Transaction description' is converted to lower case and the numbers and symbols are eliminated.
It is important to 'clean' the transaction description field as follows:
df_transactions[df_transactions['Transaction description'].str.contains('DOLLARAMA')].head()
# lowercase
df_transactions['Transaction description'] = df_transactions['Transaction description'].str.lower()
# Erasing special characters
df_transactions['Transaction description'] = df_transactions['Transaction description'].str.replace('*', ' ')
df_transactions['Transaction description'] = df_transactions['Transaction description'].str.replace('.', ' ')
df_transactions['Transaction description'] = df_transactions['Transaction description'].apply(lambda x: re.sub(r'[^a-zA-Z\s\']+', ' ', x))
df_transactions[df_transactions['Transaction description'].str.contains('dollarama')].head()
# Removing stop words
nltk.download('stopwords')
from nltk.corpus import stopwords
stop_words = set(stopwords.words('english'))
df_transactions['Transaction description'] = df_transactions['Transaction description'].apply(lambda x: [word for word in x.split() if word not in stop_words])
df_transactions['Transaction description'] = [' '.join(map(str, l)) for l in df_transactions['Transaction description']]
df_transactions.head()
df_transactions.shape
First I am counting the words by splitting them to have an initial idea of the most used words. Finally, I use CountVectorizer with 1 to 3 ngrams to vectorize the 'Transaction description' field
# One way to count words
word_count = df_transactions['Transaction description'].str.split(expand=True).stack().value_counts()
word_count = pd.DataFrame(data=word_count, columns=['Count'])
word_count = word_count.sort_values(['Count'], ascending=False)
print('- The 10 most used words are:')
most_used = word_count[:10]
sns.barplot(x=most_used['Count'], y=most_used.index)
# The transaction description field
documents = df_transactions['Transaction description'].values
no_features = 1500
# LDA can only use raw term counts for LDA because it is a probabilistic graphical model
tf_vectorizer = CountVectorizer(ngram_range=(1, 3), max_df=0.95, min_df=2, max_features=no_features) # Using count vectorized with 1 to 3 ngrams to count the words
tf = tf_vectorizer.fit_transform(documents)
tf_feature_names = tf_vectorizer.get_feature_names()
A first approach varying the number of topics to see how the perplexity and score changes was done. Then a GridSearchCV was applied to select the best conditions for the model
topics = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
perplexityResult = []
scoreResult = []
for no_topics in topics:
# Run LDA
lda = LatentDirichletAllocation(n_components=no_topics, max_iter=5, learning_method='online', learning_offset=50.,random_state=0).fit(tf)
perplexityResult.append(lda.perplexity(tf))
scoreResult.append(lda.score(tf))
fig, axes = plt.subplots(1, 2, figsize=(10, 5))
fig.subplots_adjust(hspace=0.5)
sns.despine(left=True)
sns.lineplot(topics, perplexityResult, ax=axes[0])
sns.lineplot(topics, scoreResult, ax=axes[1])
axes[0].set_title('Perplexity')
axes[0].set_xlabel('Topics')
axes[0].set_ylabel('Perplexity')
axes[1].set_title('Score')
axes[1].set_xlabel('Topics')
axes[1].set_ylabel('Score')
# Define Search Param
search_params = {'n_components': [10, 12, 15, 20], 'learning_decay': [.5, .7, .9]}
# Init the Model
lda = LatentDirichletAllocation(max_iter=5, learning_method='online', learning_offset=50., random_state=0)
# Init Grid Search Class
model = GridSearchCV(lda, param_grid=search_params)
# Do the Grid Search
model.fit(tf)
# Best Model
best_lda_model = model.best_estimator_
# Model Parameters
print("Best Model's Params: ", model.best_params_)
# Log Likelihood Score
print("Best Log Likelihood Score: ", model.best_score_)
# Perplexity
print("Model Perplexity: ", best_lda_model.perplexity(tf))
def display_topics(model, feature_names, no_top_words):
for topic_idx, topic in enumerate(model.components_):
print("Topic %d:" % (topic_idx))
print(" - ".join([feature_names[i]
for i in topic.argsort()[:-no_top_words - 1:-1]]))
selectedTopics = 10
learning_decay = 0.5
lda = LatentDirichletAllocation(n_components= selectedTopics, learning_decay = learning_decay, max_iter=5, learning_method='online', learning_offset=50.,random_state=0).fit(tf)
no_top_words = 10
display_topics(lda, tf_feature_names, no_top_words)
import pyLDAvis.sklearn
panel = pyLDAvis.sklearn.prepare(lda, tf, tf_vectorizer, mds='tsne')
pyLDAvis.display(panel)
df_lda = pd.DataFrame(data=lda.transform(tf))
dominant_topic = np.argmax(df_lda.values, axis=1)
df_lda['Dominant topic'] = dominant_topic
df_result = pd.concat([df_transactions, df_lda[['Dominant topic']]], axis=1, sort=False)
df_result.head()
fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(20, 6))
dataPlot_bank = df_result.copy()
dataPlot_bank = dataPlot_bank[ (dataPlot_bank['Original DF'] == 'Banking') & (dataPlot_bank['Year'] != 2018)]
dataPlot_bank = dataPlot_bank.drop(columns=['Month', 'Week', 'Weekday', 'Day'])
dataPlot_bank = dataPlot_bank.groupby(['Year', 'Dominant topic']).sum()
dataPlot_bank['Quantity'].unstack().plot(kind='barh', stacked=False, ax=axes[0])
dataPlot_CC1 = df_result.copy()
dataPlot_CC1 = dataPlot_CC1[ (dataPlot_CC1['Original DF'] == 'Credit Card - Bank 1') & (dataPlot_CC1['Year'] != 2018)]
dataPlot_CC1 = dataPlot_CC1.drop(columns=['Month', 'Week', 'Weekday', 'Day'])
dataPlot_CC1 = dataPlot_CC1.groupby(['Year', 'Dominant topic']).sum()
dataPlot_CC1['Quantity'].unstack().plot(kind='barh', stacked=False, ax=axes[1])
dataPlot_CC2 = df_result.copy()
dataPlot_CC2 = dataPlot_CC2[ (dataPlot_CC2['Original DF'] == 'Credit Card - Bank 2') & (dataPlot_CC2['Year'] != 2018)]
dataPlot_CC2 = dataPlot_CC2.drop(columns=['Month', 'Week', 'Weekday', 'Day'])
dataPlot_CC2 = dataPlot_CC2.groupby(['Year', 'Dominant topic']).sum()
dataPlot_CC2['Quantity'].unstack().plot(kind='barh', stacked=False, ax=axes[2] )
axes[0].set_xlabel('Quantity (CAD)')
axes[1].set_xlabel('Quantity (CAD)')
axes[2].set_xlabel('Quantity (CAD)')
axes[0].set_title('Banking')
axes[1].set_title('Credit Card 1')
axes[2].set_title('Credit Card 2')
# Year 2019
dataPlot02_2019 = df_result.copy()
dataPlot02_2019 = dataPlot02_2019[ (dataPlot02_2019['Quantity'] < 0) & (dataPlot02_2019['Year'] == 2019)]
dataPlot02_2019 = dataPlot02_2019.drop(columns=['Month', 'Week', 'Weekday', 'Day', 'Transaction description', 'Transaction Date', 'Original DF', 'Year'])
# Set expenses as positive values
dataPlot02_2019['Quantity'] = - dataPlot02_2019['Quantity']
dataPlot02_2019 = dataPlot02_2019.groupby(['Dominant topic']).sum()
# Year 2020
dataPlot02_2020 = df_result.copy()
dataPlot02_2020 = dataPlot02_2020[ (dataPlot02_2020['Quantity'] < 0) & (dataPlot02_2020['Year'] == 2020)]
dataPlot02_2020 = dataPlot02_2020.drop(columns=['Month', 'Week', 'Weekday', 'Day', 'Transaction description', 'Transaction Date', 'Original DF', 'Year'])
# Set expenses as positive values
dataPlot02_2020['Quantity'] = - dataPlot02_2020['Quantity']
dataPlot02_2020 = dataPlot02_2020.groupby(['Dominant topic']).sum()
fig = make_subplots(rows=1, cols=2, specs=[[{'type':'domain'}, {'type':'domain'}]])
labels=dataPlot02_2019['Quantity'].index.values
fig.add_trace(go.Pie(labels=labels, values=dataPlot02_2019['Quantity'].values, name="2019 Expenses"),
1, 1)
fig.add_trace(go.Pie(labels=labels, values=dataPlot02_2020['Quantity'].values, name="2020 Expenses"),
1, 2)
fig.update_traces(hole=.4, hoverinfo="label+percent+name")
fig.update_layout(
title_text="Expenses by Topic (2019 vs. 2020)",
# Add annotations in the center of the donut pies.
annotations=[dict(text='2019', x=0.20, y=0.5, font_size=20, showarrow=False),
dict(text='2020', x=0.80, y=0.5, font_size=20, showarrow=False)])
dataPlot02_2019['Quantity'].index.values
categories_CC2
coffee01 = df_result[df_result['Transaction description'].str.contains("tim's")]['Quantity'].sum()
coffee02 = df_result[df_result['Transaction description'].str.contains("coffee")]['Quantity'].sum()
coffee03 = df_result[df_result['Transaction description'].str.contains("starbucks")]['Quantity'].sum()
coffee04 = df_result[df_result['Transaction description'].str.contains("juan")]['Quantity'].sum()
Total_coffee = coffee01 + coffee02 + coffee03 + coffee04
Total_coffee
Topics 0, 6 and 9 present the higher reduction from 2019 to 2020. This can be explained as my miscellaneous payments and purchases at Costco or Marshalls have decreased due to COVID 19.
Topics 4 and 8 have increased from 2019 to 2020. This can be explained as I have spend more money in groceries and now I do more transfers via eTransfer
I love coffee, so I was interested to know how much I have spent in coffee since Dec. 1, 2018 --> the answer is 172.89 CAD