import numpy as np
import pandas as pd
import json
import re
import os
import math
import random
from collections import Counter
import matplotlib.pyplot as plt
%matplotlib inline
# Define directories
homedir = os.path.expanduser("~")
projdir = os.path.join(homedir,'Documents','Research','Trajectories')
datadir = os.path.join(projdir,'data_mm')
figsdir = os.path.join(projdir,'figures')
# Load into Pandas
mm_motifs3_fm = os.path.join(datadir,"c3_motifs.csv")
mm_motifs4_fm = os.path.join(datadir,"c4_motifs.csv")
mm_motifs3 = pd.read_csv(mm_motifs3_fm).drop(columns=['users'])
mm_motifs4 = pd.read_csv(mm_motifs4_fm).drop(columns=['users'])
mm_savings_fn = os.path.join(datadir,"users_savings.csv")
mm_savings = pd.read_csv(mm_savings_fn).sort_values(["user_ID","days"]).reset_index(drop=True)
# Convert amount to USD at PPP in 2016
ppp = XXXX
mm_motifs3["amount"] = mm_motifs3.amount.divide(ppp)
mm_motifs4["amount"] = mm_motifs4.amount.divide(ppp)
mm_savings["amt"] = mm_savings.amt.divide(ppp)
mm_savings["amt_c"] = mm_savings.amt_c.divide(ppp)
mm_motifs3.head()
mm_motifs4.head()
mm_savings.head()
# Use c3, but split off the sterotypical transfer motif
mm_motifs = mm_motifs3[~(mm_motifs3['motif']=='cash-dep~circulate~cash-wtd')]
mm_motifs = mm_motifs.append(mm_motifs4[mm_motifs4['motif']=='cash-dep~circulate~cash-wtd'])
mm_motifs = mm_motifs.append(mm_motifs4[mm_motifs4['motif']=='cash-dep~p2p~cash-wtd'])
# Calculate the total
mm_motifs = mm_motifs.append(mm_motifs.sum(numeric_only=True), ignore_index=True)
mm_motifs.loc[mm_motifs.index[-1],'motif'] = "total"
mm_motifs[mm_motifs['motif']=="total"]
# Normalize the relevant columns
mm_motifs['amount_frac'] = mm_motifs['amount'].apply(lambda x: x/mm_motifs.loc[mm_motifs.index[-1],'amount'])
mm_motifs['deposits_frac'] = mm_motifs['deposits'].apply(lambda x: x/mm_motifs.loc[mm_motifs.index[-1],'deposits'])
mm_motifs['flows_frac'] = mm_motifs['flows'].apply(lambda x: x/mm_motifs.loc[mm_motifs.index[-1],'flows'])
# Get the total share of ~single-use~ funds
deposits = ["cash-dep","bank-dep"]
exits = ["cash-wtd","bank-wtd","cash-pay","bill-pay","mins-pay"]
single_use = ["cash-dep~p2p~cash-wtd"]+[dep+"~"+wtd for dep in deposits for wtd in exits]
one = sum(mm_motifs[mm_motifs["motif"].isin(single_use)]["amount_frac"])
one
# Get the total ~circulating~ funds
circulating = [dep+"~circulate~"+wtd for dep in deposits for wtd in exits]
circ = sum(mm_motifs[mm_motifs["motif"].isin(circulating)]["amount_frac"])
circ
# Get the total ~remaining~ funds
rem = sum(mm_motifs[mm_motifs["motif"].str.contains("inferred")]["amount_frac"])
rem
# Confirm total
one + circ + rem
# Avoid smaller and idiosyncratic services
mm_motifs_filtered = mm_motifs[mm_motifs["flows_frac"]>0.01]
mm_motifs_filtered
# Let's limit ourselves to users with three or more transactions of turnover
consistent_users = mm_savings[mm_savings['txn_c']>=3]['user_ID'].unique()
n_consistent_users = len(consistent_users)
mm_savings_consistent = mm_savings[mm_savings['user_ID'].isin(consistent_users)]
# Get the longest save duration, by various cutoffs, for each user
def max_save_duration(df, weight="amt_c", min_val=0):
label = weight+">"+str(min_val)
return df[df[weight]>min_val].groupby('user_ID').agg({'days':max}).squeeze().rename(label)
# Amount cutoffs
cutoffs = {
"amt_c" : [0,1,10,100,1000],
"txn_c" : [0,0.5,1,2,5],
"amt_cr" : [0,0.01,0.05,0.20]
}
cutoff_list = []
for term in cutoffs:
cutoff_list = cutoff_list+[(term,val) for val in cutoffs[term]]
mm_savings_user = pd.concat([max_save_duration(mm_savings_consistent,weight=term,min_val=val) for term, val in cutoff_list],axis=1)
mm_savings_user.head()
# Summarize by day
mm_savings_days = mm_savings_user.apply(lambda x: x.value_counts())
mm_savings_days = mm_savings_days.apply(lambda x: x/n_consistent_users)
# Plot the number of users who saved more than zero for longer than X days
fig = plt.figure()
ax = fig.add_axes([0,0,1,1])
ax.bar(mm_savings_days.index,mm_savings_days.loc[::-1,'amt_cr>0.05'].cumsum()[::-1])
plt.show()
# Figure
fig = plt.figure(dpi=200,figsize=(4,3))
ax = fig.add_axes([0,0,1,1],frameon=False)
# Savings accumulation duration label
save_lengths = [str(dur) for dur in range(31)]+['31+']
# Plotted series
savings_gt0 = mm_savings_days.loc[::-1,'amt_cr>0'].cumsum()[::-1]
savings_gt5 = mm_savings_days.loc[::-1,'amt_cr>0.01'].cumsum()[::-1]
savings_gt10 = mm_savings_days.loc[::-1,'amt_cr>0.05'].cumsum()[::-1]
savings_gt25 = mm_savings_days.loc[::-1,'amt_cr>0.2'].cumsum()[::-1]
ax.plot(save_lengths,savings_gt0,marker='o',color='firebrick',linewidth=0.5,label='> 0%')
ax.plot(save_lengths,savings_gt5,marker='8',color='teal',linewidth=0.5,label='> 1%')
ax.plot(save_lengths,savings_gt10,marker='h',color='skyblue',linewidth=0.5,label='> 5%')
ax.plot(save_lengths,savings_gt25,marker='p',color='gold',linewidth=0.5,label='>20%')
# Percentages on y-axis
ax.set_ylim(0,1.05)
ax.grid(axis='y', color='lightgrey', linestyle='--', linewidth=0.75)
y_vals = ax.get_yticks()
ax.set_yticklabels(['{:,.0%}'.format(x) for x in y_vals])
ax.set_ylabel('Share of accounts', fontsize=12, labelpad=6)
# Days on x-axis
ax.set_axisbelow(True)
ax.grid(axis='x', color='lightgrey', linestyle='--', linewidth=0.75)
ax.set_xticks(['0','7','14','21','28','31+'])
ax.set_xlabel('Days since funds received', fontsize=12, labelpad=6)
# Legend for savings worth
ax.legend(title='Funds remaining:',loc='upper right',bbox_to_anchor=(1.1, 0.87),numpoints=2,borderaxespad=1)
# Save the plot!
plt.savefig(os.path.join(figsdir,'MM_accounts_save.pdf'),dpi=300, bbox_inches='tight')
# Figure
fig = plt.figure(dpi=200,figsize=(4,3))
ax = fig.add_axes([0,0,1,1],frameon=False)
# Savings accumulation duration label
save_lengths = [str(dur) for dur in range(31)]+['31+']
# Plotted series
savings_gt0 = mm_savings_days.loc[::-1,'amt_c>0'].cumsum()[::-1]
savings_gt5 = mm_savings_days.loc[::-1,'amt_c>1'].cumsum()[::-1]
savings_gt10 = mm_savings_days.loc[::-1,'amt_c>10'].cumsum()[::-1]
savings_gt25 = mm_savings_days.loc[::-1,'amt_c>100'].cumsum()[::-1]
ax.plot(save_lengths,savings_gt0,marker='o',color='firebrick',linewidth=0.5,label='> 0$PPP')
ax.plot(save_lengths,savings_gt5,marker='8',color='teal',linewidth=0.5,label='> 1$PPP')
ax.plot(save_lengths,savings_gt10,marker='h',color='skyblue',linewidth=0.5,label='> 10$PPP')
ax.plot(save_lengths,savings_gt25,marker='p',color='gold',linewidth=0.5,label='>100$PPP')
# Percentages on y-axis
ax.set_ylim(0,1.05)
ax.grid(axis='y', color='lightgrey', linestyle='--', linewidth=0.75)
y_vals = ax.get_yticks()
ax.set_yticklabels(['{:,.0%}'.format(x) for x in y_vals])
ax.set_ylabel('Share of accounts', fontsize=12, labelpad=6)
# Days on x-axis
ax.set_axisbelow(True)
ax.grid(axis='x', color='lightgrey', linestyle='--', linewidth=0.75)
ax.set_xticks(['0','7','14','21','28','31+'])
ax.set_xlabel('Days since funds received', fontsize=12, labelpad=6)
# Legend for savings worth
ax.legend(title='Funds remaining:',loc='upper right',bbox_to_anchor=(0.43, 0.43),numpoints=2,borderaxespad=1)
# Save the plot!
plt.savefig(os.path.join(figsdir,'MM_accounts_save_abs.pdf'),dpi=300, bbox_inches='tight')
# Figure
fig = plt.figure(dpi=200,figsize=(4,3))
ax = fig.add_axes([0,0,1,1],frameon=False)
# Savings accumulation duration label
save_lengths = [str(dur) for dur in range(31)]+['31+']
# Plotted series
savings_gt0 = mm_savings_days.loc[::-1,'txn_c>0'].cumsum()[::-1]
savings_gt5 = mm_savings_days.loc[::-1,'txn_c>0.5'].cumsum()[::-1]
savings_gt10 = mm_savings_days.loc[::-1,'txn_c>1'].cumsum()[::-1]
savings_gt25 = mm_savings_days.loc[::-1,'txn_c>2'].cumsum()[::-1]
ax.plot(save_lengths,savings_gt0,marker='o',color='firebrick',linewidth=0.5,label='>0.0txn')
ax.plot(save_lengths,savings_gt5,marker='8',color='teal',linewidth=0.5,label='>0.5txn')
ax.plot(save_lengths,savings_gt10,marker='h',color='skyblue',linewidth=0.5,label='>1.0txn')
ax.plot(save_lengths,savings_gt25,marker='p',color='gold',linewidth=0.5,label='>2.0txn')
# Percentages on y-axis
ax.set_ylim(0,1.05)
ax.grid(axis='y', color='lightgrey', linestyle='--', linewidth=0.75)
y_vals = ax.get_yticks()
ax.set_yticklabels(['{:,.0%}'.format(x) for x in y_vals])
ax.set_ylabel('Share of accounts', fontsize=12, labelpad=6)
# Days on x-axis
ax.set_axisbelow(True)
ax.grid(axis='x', color='lightgrey', linestyle='--', linewidth=0.75)
ax.set_xticks(['0','7','14','21','28','31+'])
ax.set_xlabel('Days since funds received', fontsize=12, labelpad=6)
# Legend for savings worth
ax.legend(title='Funds remaining:',loc='upper right',bbox_to_anchor=(1.1, 0.87),numpoints=2,borderaxespad=1)
# Save the plot!
plt.savefig(os.path.join(figsdir,'MM_accounts_save_txn.pdf'),dpi=300, bbox_inches='tight')
! jupyter nbconvert --to html mobile_money.ipynb