1.1
Charge the libraries
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ forcats 1.0.0 ✔ readr 2.1.4
## ✔ ggplot2 3.4.4 ✔ stringr 1.5.1
## ✔ lubridate 1.9.3 ✔ tibble 3.2.1
## ✔ purrr 1.0.2 ✔ tidyr 1.3.0
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(lubridate)
library(conflicted)
Get all the data
ds_deposit <- read.csv("D:/Bitso Challenge/databases_csv/ds_deposit.csv", header = FALSE, as.is = TRUE)
ds_withdrawal <- read.csv("D:/Bitso Challenge/databases_csv/ds_withdrawal.csv", header = FALSE, as.is = TRUE)
ds_trade <- read.csv("D:/Bitso Challenge/databases_csv/ds_trade.csv", header = FALSE, as.is = TRUE)
ds_price_data <- read.csv("D:/Bitso Challenge/databases_csv/ds_price_data.csv", as.is = TRUE)
options(scipen = 999)
Deposit cleaning
colnames(ds_deposit) <- ds_deposit[1, ]
ds_deposit = rename(ds_deposit,c("NA","id" = "NA"))
ds_deposit <- ds_deposit[-c(1),]
rownames(ds_deposit) <- NULL
Find users that deposit per week
ds_deposit$created <-
as.Date(ds_deposit$created,format = "%Y-%m-%j")
ds_deposit$Week <-
strftime(c(ds_deposit$created), format = "%V")
Create a table to show User deposits x week
Deposits_Week <-
ds_deposit %>%
group_by(Week) %>%
summarize(Users_That_Deposit = n_distinct(user_id))
Clean ds_withdrawal
colnames(ds_withdrawal) <- ds_withdrawal[1, ]
ds_withdrawal = rename(ds_withdrawal,c("NA","id" ="NA"))
ds_withdrawal <- ds_withdrawal[-c(1), ]
rownames(ds_withdrawal) <- NULL
Find users that withdrawal per week
ds_withdrawal$created <-
as.Date(ds_withdrawal$created,format = "%Y-%m-%j")
ds_withdrawal$Week <-
strftime(c(ds_withdrawal$created), format = "%V")
Create a table to show User Withdrawal x week
Withdrawal_Week <-
ds_withdrawal %>%
group_by(Week) %>%
summarize(Users_That_withdrawal = n_distinct(user_id))
Merge tables to show both
This way you can have the info. separately and also merged.
Deposits_Withdrawal_Week <-
merge(Withdrawal_Week,Deposits_Week,by = "Week")
print(Deposits_Withdrawal_Week)
## Week Users_That_withdrawal Users_That_Deposit
## 1 49 16 18
## 2 50 14 11
## 3 51 11 11
## 4 52 3 7
## 5 53 4 5
1.2
Declare a new column in both tables to classify the transaction
ds_deposit$type <- "deposit"
ds_withdrawal$type <- "withdrawal"
Change the datatype
ds_deposit$amount <- as.numeric(as.character(ds_deposit$amount))
Rename the date to merge both tables
ds_deposit = rename(ds_deposit,c("created","date" = "created"))
Get the deposit amount in usd
Deposit_in_Usd<- merge(ds_deposit, ds_price_data, by = "date", all.x = TRUE)
convert_to_usd <- function(currency, amount, usd) {
if (currency == "usd") {
return(amount)
} else {
return(amount * usd)
}
}
Apply the function
Deposit_in_Usd$amount_usd <- mapply(convert_to_usd,
Deposit_in_Usd$currency,
Deposit_in_Usd$amount,
Deposit_in_Usd$usd)
Change the amount datatype
ds_withdrawal$amount <- as.numeric(as.character(ds_withdrawal$amount))
Rename the date to merge both tables
ds_withdrawal = rename(ds_withdrawal,c("created","date" = "created"))
Get the withdrawal amount in usd
Withdrawal_in_Usd<- merge(ds_withdrawal, ds_price_data, by = "date", all.x = TRUE)
convert_to_usd <- function(currency, amount, usd) {
if (currency == "usd") {
return(amount)
} else {
return(amount * usd)
}
}
Apply the function
Withdrawal_in_Usd$amount_usd <- mapply(convert_to_usd,
Withdrawal_in_Usd$currency,
Withdrawal_in_Usd$amount,
Withdrawal_in_Usd$usd)
Get a new table to get the net deposit amount per week
all_transactions <- rbind(Deposit_in_Usd, Withdrawal_in_Usd)
net_deposit <- sum(all_transactions$amount * ifelse(all_transactions$type == "deposit", 1, -1))
Summarize the Net Deposit
net_deposit <- all_transactions %>%
group_by(Week) %>%
summarise(net_deposit_usd = sum(amount_usd))
net_deposit$net_deposit_usd <- format(net_deposit$net_deposit_usd, big.mark = ".", decimal.mark = ",")
print(net_deposit)
## # A tibble: 5 × 2
## Week net_deposit_usd
## <chr> <chr>
## 1 49 " 268.662.822.487"
## 2 50 " 53.405.045.688"
## 3 51 " 345.336.619.382"
## 4 52 "9.747.567.116.848"
## 5 53 "1.912.651.343.516"
1.3
Create the function to categorize the currencies
classify_currency <- function(currency) {
# List of known fiat currencies
fiat_currencies <- c("usd", "mxn", "ars", "brl")
# List of known cryptocurrencies
crypto_currencies <- c("btc", "eth", "xrp", "ltc", "bch","bat","dai","gnt","mana","tusd","etc","rbtc","bsv") # Add more if needed
# Check if the currency is fiat or crypto
if (currency %in% fiat_currencies) {
return("Fiat")
} else if (currency %in% crypto_currencies) {
return("Crypto")
} else {
return("Unknown")
}
}
Apply an showing the info.
ds_deposit$currency_type <- sapply(ds_deposit$currency, classify_currency)
head(ds_deposit)
1.4
Merging 2 tables to get all transactions per day per user
ds_deposit <- subset(ds_deposit, select = -currency_type)
all_transactions <- rbind(ds_deposit, ds_withdrawal)
# get active users per week
active_users_per_week <- aggregate(
user_id ~ Week,
data = all_transactions,
FUN = function(x) length(unique(x)))
Change the column names
colnames(active_users_per_week) <- c("week", "active_users")
print(active_users_per_week)
## week active_users
## 1 49 28
## 2 50 22
## 3 51 18
## 4 52 10
## 5 53 8
2.1
Clean the table
colnames(ds_trade) <- ds_trade[1, ]
ds_trade <- ds_trade[-c(1), ]
rownames(ds_trade) <- NULL
ds_trade$created <- as.Date(ds_trade$created,format = "%Y-%m-%j")
ds_trade = rename(ds_trade,c("created","date" = "created"))
Change the datatype
ds_price_data$usd <- as.numeric(ds_price_data$usd)
ds_trade$major_fee <- as.numeric(ds_trade$major_fee)
ds_trade$minor_fee <- as.numeric(ds_trade$minor_fee)
Merge the tables to get the seller fee in usd
Seller_fee <- merge(ds_trade, ds_price_data, by = c("date", "major_currency"), all.x = TRUE)
Seller_fee <- mutate(Seller_fee, Fee_usd_seller = ifelse(major_currency == "usd", major_fee, major_fee * usd))
Merge the table to get the buyer fee in usd
ds_price_data_minor <- rename(ds_price_data, minor_currency = major_currency)
ds_trade$major_fee <- as.numeric(ds_trade$major_fee)
Buyer_fee <- merge(ds_trade, ds_price_data_minor, by = c("date", "minor_currency"), all.x = TRUE)
Buyer_fee <- mutate(Buyer_fee, Fee_usd_buyer = ifelse(minor_currency == "usd", minor_fee, minor_fee * usd))
Change the date to week and sumarizing the fees
Seller_fee_week <- Seller_fee %>%
mutate(week = lubridate::week(date)) %>%
group_by(week) %>%
summarize(Fee_usd_seller_total = sum(Fee_usd_seller, na.rm = TRUE))
Buyer_fee_week <- Buyer_fee %>%
mutate(week = lubridate::week(date)) %>%
group_by(week) %>%
summarize(Fee_usd_buyer_total = sum(Fee_usd_buyer, na.rm = TRUE))
Merge both in 1 table
Revenue_week <- merge(Seller_fee_week, Buyer_fee_week, by = "week", all = TRUE) %>%
mutate(Total_Fee_usd = Fee_usd_seller_total + Fee_usd_buyer_total) %>%
select(-Fee_usd_seller_total, -Fee_usd_buyer_total)
Show the result
Revenue_per_week <- Revenue_week
Revenue_week$Total_Fee_usd <- format(Revenue_week$Total_Fee_usd, big.mark = ".", decimal.mark = ",")
print(Revenue_week)
## week Total_Fee_usd
## 1 48 33.308,28
## 2 49 58.820,51
## 3 50 124.393,10
## 4 51 275.450,62
## 5 52 112.611,67
## 6 53 54.631,84
2.2
Make a new column to show the date in weeks
ds_trade$Week <- strftime(c(ds_trade$date), format = "%V")
Make a table to show paying users
paying_users <-
ds_trade %>%
group_by(Week) %>%
summarize(buyers = ifelse(amount != 0,major_user_id,0),
sellers = ifelse(value != 0,minor_user_id,0))
## Warning: Returning more (or less) than 1 row per `summarise()` group was deprecated in
## dplyr 1.1.0.
## ℹ Please use `reframe()` instead.
## ℹ When switching from `summarise()` to `reframe()`, remember that `reframe()`
## always returns an ungrouped data frame and adjust accordingly.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## `summarise()` has grouped output by 'Week'. You can override using the
## `.groups` argument.
Make a new table to show the sum of paying users per week
result <-
paying_users %>%
group_by(Week) %>%
summarize(paying_users= n_distinct(buyers,sellers))
print(result)
## # A tibble: 5 × 2
## Week paying_users
## <chr> <int>
## 1 49 53
## 2 50 44
## 3 51 46
## 4 52 54
## 5 53 39
2.3
Weekly ARPU
buyer <- ds_trade %>%
group_by(Week) %>%
summarise(user = minor_user_id)
## Warning: Returning more (or less) than 1 row per `summarise()` group was deprecated in
## dplyr 1.1.0.
## ℹ Please use `reframe()` instead.
## ℹ When switching from `summarise()` to `reframe()`, remember that `reframe()`
## always returns an ungrouped data frame and adjust accordingly.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## `summarise()` has grouped output by 'Week'. You can override using the
## `.groups` argument.
seller <- ds_trade %>%
group_by(Week) %>%
summarise(user = major_user_id)
## Warning: Returning more (or less) than 1 row per `summarise()` group was deprecated in
## dplyr 1.1.0.
## ℹ Please use `reframe()` instead.
## ℹ When switching from `summarise()` to `reframe()`, remember that `reframe()`
## always returns an ungrouped data frame and adjust accordingly.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## `summarise()` has grouped output by 'Week'. You can override using the
## `.groups` argument.
users <- rbind(buyer,seller)
users <- users %>%
group_by(Week) %>%
summarise(users = n_distinct(user))
Change the dataype
users$Week <- as.factor(users$Week)
names(Revenue_per_week)[names(Revenue_per_week) == "week"] <- "Week"
Revenue_per_week$Week <- as.factor(Revenue_per_week$Week)
# Convertir las columnas a los tipos de datos adecuados en 'Revenue_per_week'
Revenue_week <- as.data.frame(lapply(Revenue_per_week, type.convert))
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
## Warning in type.convert.default(X[[i]], ...): 'as.is' should be specified by
## the caller; using TRUE
# Fusionar 'Revenue_per_week' y 'users' por la columna "Week"
revenue_per_user <- merge(Revenue_week, users, by = "Week")
# Verificar la estructura de 'revenue_per_user' después de la fusión
str(revenue_per_user)
## 'data.frame': 5 obs. of 3 variables:
## $ Week : int 49 50 51 52 53
## $ Total_Fee_usd: num 58821 124393 275451 112612 54632
## $ users : int 24 13 17 18 11
# Convertir la columna 'Total_Fee_usd' a numérico
revenue_per_user$Total_Fee_usd <- as.numeric(revenue_per_user$Total_Fee_usd)
# Calcular ARPU (ingreso promedio por usuario activo)
ARPU <- revenue_per_user %>%
group_by(Week) %>%
summarise(ARPU = sum(Total_Fee_usd) / sum(users))
# Formatear ARPU para mostrar números con separador de miles y coma decimal
ARPU$ARPU <- format(ARPU$ARPU, big.mark = ".", decimal.mark = ",")
# Imprimir ARPU
print(ARPU)
## # A tibble: 5 × 2
## Week ARPU
## <int> <chr>
## 1 49 " 2.450,855"
## 2 50 " 9.568,700"
## 3 51 "16.202,978"
## 4 52 " 6.256,204"
## 5 53 " 4.966,531"
weekly ARPPU
Change the datatype
names(Revenue_per_week)[names(Revenue_per_week) == "week"] <- "Week"
Revenue_per_week$Total_Fee_usd <- as.numeric(Revenue_per_week$Total_Fee_usd)
result$paying_users <- as.numeric(result$paying_users)
result$Week <- as.factor(result$Week)
Revenue_per_week$Week <- as.factor(Revenue_per_week$Week)
names(Revenue_per_week)[names(Revenue_per_week) == "week"] <- "Week"
Get the ARPPU
ARPPU <- merge(Revenue_per_week, result, by = "Week") %>%
group_by(Week) %>%
summarise(ARPPU = sum(Total_Fee_usd) / sum(paying_users))
ARPPU$ARPPU <- format(ARPPU$ARPPU, big.mark = ".", decimal.mark = ",")
print(ARPPU)
## # A tibble: 5 × 2
## Week ARPPU
## <fct> <chr>
## 1 49 1.109,821
## 2 50 2.827,116
## 3 51 5.988,057
## 4 52 2.085,401
## 5 53 1.400,816
Bonus
Import the dataframe
ds_user_balance <- read.csv("D:/Bitso Challenge/databases_csv/ds_user_balance.csv", as.is = TRUE)
Charge the libraries
library(dplyr)
library(ggplot2)
Change the datatype
ds_user_balance$date <- as.Date(ds_user_balance$date)
Make the categories
df_summary <- ds_user_balance %>%
mutate(month = format(date, "%Y-%m")) %>%
group_by(month, user_id) %>%
summarize(
balance_positive = sum(balance_usd > 0),
balance_previous_positive = sum(dplyr::lag(balance_usd, default = 0) > 0),
balance_previous_zero = sum(dplyr::lag(balance_usd, default = 0) == 0),
balance_zero = sum(balance_usd == 0)
) %>%
mutate(category = case_when(
balance_positive > 0 & balance_previous_positive == 0 ~ "New user",
balance_positive > 0 & balance_previous_positive > 0 ~ "Steady user",
balance_zero > 0 & balance_previous_positive > 0 ~ "Lost user",
balance_positive > 0 & balance_previous_zero > 0 ~ "Recovered user",
TRUE ~ "Other"
)) %>%
ungroup() %>%
group_by(month, category) %>%
summarize(users = n()) %>%
mutate(percentage = users / sum(users) * 100)
## `summarise()` has grouped output by 'month'. You can override using the
## `.groups` argument.
## `summarise()` has grouped output by 'month'. You can override using the
## `.groups` argument.
Make the plot
ggplot(df_summary, aes(x = month, y = percentage, fill = category)) +
geom_bar(stat = "identity", position = "stack") +
scale_fill_manual(values = c("New user" = "blue", "Steady user" = "green", "Lost user" = "red", "Recovered user" = "orange", "Other" = "gray")) +
labs(
title = "Porcentaje de usuarios por categoría",
y = "Porcentaje de usuarios",
x = "Mes",
fill = "Categoría"
) +
theme_minimal()
