Splitwise API for Personal Expenditure Analytics in Python
Are you looking to gain a better understanding of your personal expenses and track your spending habits?
Well, if you are using the Splitwise app for expense management within groups, you will find this post helpful.
In this blog post, I’ll walk through how to use the Splitwise API to analyze personal expenses and create useful visualizations using Python. I’ll cover everything from setting up the API credentials to generating informative graphs that can help you better understand your spending patterns.
Alright! Let’s get started…
Personal Data Analytics Class
Importing Modules
The following code starts with importing necessary modules for the project. The first two lines import the splitwise
module and Splitwise
class from it.
The splitwise
module provides a Python wrapper for the Splitwise API, which allows users to interact with the Splitwise platform programmatically.
Next, the code imports matplotlib
library and its pyplot
module as plt
. This is a popular data visualization library in Python, used to create interactive and informative graphs and charts.
The next lines import various date-related modules from matplotlib
and datetime
libraries, which are used to manipulate dates and times for plotting purposes.
Lastly, the code imports the pandas
library as pd
, which is a powerful and flexible data analysis and manipulation tool. It provides easy-to-use data structures and data analysis tools for handling and analyzing data efficiently.
# import modules
import splitwise
from splitwise import Splitwise
import matplotlib.pyplot as plt
from matplotlib.dates import DateFormatter, WeekdayLocator,\
DayLocator, MONDAY
import datetime
from datetime import date, datetime, timedelta
import pandas as pd
Initialize the Class
The given code defines a class called ExpenseTracker
with a constructor method __init__
.
__init__
method takes in three parameters CONSUMER_KEY
, CONSUMER_SECRET
, and API_KEY
. These parameters are used to authenticate the API of the Splitwise application.
Inside the constructor method, self.sw
object is initialized by calling Splitwise
constructor. The three parameters CONSUMER_KEY
, CONSUMER_SECRET
, and API_KEY
are passed to Splitwise
constructor to create an instance of Splitwise API.
class ExpenseTracker:
def __init__(self, CONSUMER_KEY, CONSUMER_SECRET, API_KEY):
self.sw = Splitwise(CONSUMER_KEY, CONSUMER_SECRET, api_key=API_KEY)
self.expenses = []
Fetch expense Data
This is a method called get_expenses
which is used to retrieve expenses from the Splitwise API. The method takes an optional argument target_days
which specifies the number of days to retrieve expenses for (default is 365 days).
The method starts by initializing some variables such as the current page number and the page size. It also sets the start and end dates based on the target_days
parameter.
The method then enters a loop that retrieves expenses from the API using the getExpenses
method from the splitwise
module. It passes the dated_after
and dated_before
parameters to specify the date range, limit
parameter to specify the number of expenses to retrieve in a single request, and offset
parameter to specify the starting point of the expenses to retrieve.
def get_expenses(self, target_days=365):
current_page_num = 0
page_size = 100
end_date = date.today()
start_date = end_date - timedelta(days=target_days)
while True:
curr_page_expenses = self.sw.getExpenses(
dated_after=start_date,
dated_before=end_date,
limit=page_size,
offset=current_page_num * page_size,
)
if len(curr_page_expenses) == 0:
break
current_page_num = current_page_num + 1
self.expenses = self.expenses + curr_page_expenses
return self.expenses
The retrieved expenses are then added to the self.expenses
list which is an instance variable of the ExpenseTracker
class. This process continues until there are no more expenses to retrieve.
Finally, the method returns the list of expenses retrieved from the API.
Process the Fetched Data
The following code processes all field data and construct a dataframe for our convenience. More details of methods of data extraction are available here on Splitwise Python SDK.
def process_expense(self):
# get current user ID
my_user_id = self.sw.getCurrentUser().getId()
my_expenses = []
for expense in self.expenses:
for user in expense.users:
if user.id == my_user_id and expense.deleted_at is None and expense.description != "Payment":
my_expenses.append(expense)
data = {'id': [], 'group':[], 'description': [], 'amount': [], 'my_cost':[], 'person':[],\
'currency': [], 'date': [], 'category': []}
for expense in my_expenses:
try:
data['id'].append(expense.id)
except Exception as e:
print(e)
data['id'].append("0")
try:
data['group'].append(s.getGroup(expense.group_id).name)
except Exception as e:
print(e)
data['group'].append("0")
try:
data['description'].append(expense.description)
except Exception as e:
print(e)
data['description'].append("0")
try:
data['amount'].append(expense.cost)
except Exception as e:
print(e)
data['amount'].append("0")
try:
data['my_cost'].append(next((user.getNetBalance() \
for user in expense.getUsers() if user.id == my_user_id), 0))
except Exception as e:
print(e)
data['my_cost'].append("0")
try:
data['person'].append(next((user.getFirstName()+" "+user.getLastName() \
for user in expense.getUsers() if user.id != my_user_id), 0))
except Exception as e:
print(e)
data['person'].append("0")
try:
data['currency'].append(expense.currency_code)
except Exception as e:
print(e)
data['currency'].append("0")
try:
data['date'].append(expense.date)
except Exception as e:
print(e)
data['date'].append("0")
try:
data['category'].append(expense.category.name)
except Exception as e:
print(e)
data['category'].append("0")
# create dataframe
self.df = pd.DataFrame(data)
return self.df
Fix Data Types
Since all data are in string format, we need to change some for our convenience.
# fix data types since by default all are in string formats
def fix_datatypes(self):
self.df['my_cost'] = pd.to_numeric(self.df['my_cost'])
self.df['my_cost'] = self.df['my_cost'].abs()
self.df.date = pd.to_datetime(self.df.date)
self.df.amount = pd.to_numeric(self.df.amount)
Get Group Information
Since I wanted to have analysis based on each group I belong to, I extracted group names and expenses.
# return all group names in a list
def get_group_names(self):
return self.df["group"].to_list()
# filter expense data based on particular group
def get_group_expense(self, group_name):
group_df = self.df.loc[self.df["group"] == group_name]
return group_df
Visualization
Now, it’s visualization time. The code contains several functions that generate different types of visualizations based on the expense data. Here are short summaries of each function:
plot_monthly_group_expense
: Plots the total group expense per month for a given group.plot_all_categorical_expense
: Plots the expense by category for all expenses.plot_group_categorical_expense
: Plots the expense by category for a given group.plot_monthly_personal_expense
: Plots the total personal expense per month for all expenses.plot_monthly_personal_group_expense
: Plots the total personal expense per month for a given group.plot_weekly_personal_expense
: Plots the total personal expense per week using a bar chart with dates on the x-axis.#################################### VISUALIZATION ##################################### def plot_monthly_group_expense(self, group_name): group_df = self.get_group_expense(group_name) group_df['amount'].groupby(group_df['date'].dt.to_period('M')).sum().plot(kind='bar') plt.xlabel('Month') plt.ylabel('Amount in USD') plt.title('Total group Expense per month') plt.show() def plot_all_categorical_expense(self): category_grp = self.df.groupby(self.df['category']) category_grp.amount.sum().plot(kind='bar') plt.ylabel('Amount in USD') plt.title('Expense by Category') plt.show() def plot_group_categorical_expense(self, group_name): group_df = self.get_group_expense(group_name) category_grp = group_df.groupby(group_df['category']) category_grp.amount.sum().plot(kind='bar') plt.ylabel('Amount in USD') plt.title('Expense by Category') plt.show() def plot_monthly_personal_expense(self): self.df['my_cost'].groupby(self.df['date'].dt.to_period('M')).sum().plot(kind='bar') plt.xlabel('Month') plt.ylabel('Amount in USD') plt.title('Total personal Expense per month') plt.show() def plot_monthly_personal_group_expense(self, group_name): group_df = self.get_group_expense(group_name) group_df['my_cost'].groupby(group_df['date'].dt.to_period('M')).sum().plot(kind='bar') plt.xlabel('Month') plt.ylabel('Amount in USD') plt.title('Total personal group Expense per month') plt.show() def plot_weekly_personal_expense(self): # # https://matplotlib.org/1.5.3/examples/pylab_examples/finance_demo.html mondays = WeekdayLocator(MONDAY) # major ticks on the mondays alldays = DayLocator() # minor ticks on the days weekFormatter = DateFormatter('%b %d') # e.g., Jan 12 dayFormatter = DateFormatter('%d') # e.g., 12 ax = plt.subplot(111) ax.bar(self.df.date, self.df.my_cost) ax.xaxis.set_major_locator(mondays) ax.xaxis.set_minor_locator(alldays) ax.xaxis.set_major_formatter(weekFormatter) plt.show()
Full Code
The full code is available here…
usage
The following code imports the ExpenseTracker class from the class module. Here, we create an instance of the ExpenseTracker class and passes the Splitwise API authentication keys as arguments.
Then it calls the get_expenses()
method with the argument target_days
set to 365, which retrieves all the expenses from the past year (365 days). This code is meant to be run from the command line, and it retrieves the expenses and outputs them to the console.
#!/usr/bin/env python
# -*-coding:utf-8 -*-
'''
@File : main.py
@Time : 2023/03/09
@Version : 1.0
@Contact : sroy10@uh.edu
@Desc : Main Namespace for Splitwise App
'''
# import modules
from my_splitwise import ExpenseTracker
if __name__ == '__main__':
CONSUMER_KEY = '<Put your Consumer Key>'
CONSUMER_SECRET = '<Put Your Consumer Secret>'
API_KEY = '<Put your API Key>'
my_sw = ExpenseTracker(CONSUMER_KEY,CONSUMER_SECRET,API_KEY)
# change number of days for your convenience
my_sw.get_expenses(target_days=365)
That’s all for today! Cheers guys!!! 🤘
Thank you for reading my blog post! 🙏
If you enjoyed it and would like to stay updated on my latest content and plans for next week, be sure to subscribe to my newsletter on Substack. 👇
Once a week, I’ll be sharing the latest weekly updates on my published articles, along with other news, content and resources. Enter your email below to subscribe and join the conversation! ✍️
I am also writing on Medium. You can follow me here.
Leave a comment