Au cours des discussions, une «petite» tĂąche s'est imposĂ©e: construire la dynamique de la structure du portefeuille de prĂȘts (dynamique d'une carte de crĂ©dit, par exemple). Il y a une spĂ©cificitĂ© importante - il est nĂ©cessaire d'appliquer la mĂ©thode FIFO pour rembourser les prĂȘts. Ceux. lors du remboursement, les premiers prĂȘts doivent ĂȘtre remboursĂ©s en premier. Cela impose certaines exigences sur le calcul du statut de chaque prĂȘt individuel et la dĂ©termination de sa date d'Ă©chĂ©ance.
Considérez cela comme un problÚme olympique. Pas de « prix d'énergie sanglante » et de pédalage de code, l'approche est exclusivement « penser d'abord ». Pas plus d'un écran de code par prototype et pas de boucles (intégré pour les performances et la lisibilité). Vous trouverez ci-dessous le code R avec une approche prototype.
C'est la continuation d'une série de publications précédentes .
Décomposition
Puisque nous faisons tout à partir de zéro, nous divisons la tùche en trois étapes:
- Formation des données de test.
- Calcul de la date d'Ă©chĂ©ance de chaque prĂȘt.
- Calcul et visualisation de la dynamique pour une fenĂȘtre temporelle donnĂ©e.
HypothĂšses et dispositions pour le prototype:
- GranularitĂ© Ă jour. Une seule transaction Ă une date. S'il y a plusieurs transactions en une journĂ©e, alors leur ordre devra ĂȘtre Ă©tabli (pour se conformer au principe FIFO). Vous pouvez utiliser add. index, vous pouvez utiliser unixtimestamp, vous pouvez trouver autre chose. Cela n'a pas d'importance pour le prototype.
- Il
for
ne devrait pas y avoir de boucles explicites . Il ne devrait y avoir aucune copie inutile. Concentrez-vous sur une consommation de mémoire minimale et des performances maximales. - Nous considérerons les groupes de retards suivants: "<0", "0-30", "31-60", "61-90", "90+".
Ătape 1. GĂ©nĂ©ration de l'ensemble de donnĂ©es
Juste un jeu de donnĂ©es de test, toutes les correspondances sont alĂ©atoires. Pour chaque utilisateur, nous gĂ©nĂ©rerons ~ 10 enregistrements. Pour les calculs, nous supposons que le prĂȘt est une valeur positive, le remboursement est nĂ©gatif. Et le cycle de vie complet de chaque utilisateur doit commencer par un prĂȘt.
library(tidyverse)
library(lubridate)
library(magrittr)
library(tictoc)
library(data.table)
total_users <- 100
events_dt <- tibble(
date = sample(
seq.Date(as.Date("2021-01-01"), as.Date("2021-04-30"), by = "1 day"),
total_users * 10,
replace = TRUE)
) %>%
# 50 .
mutate(amount = (runif(n(), -2000, 1000)) %/% 50 * 50) %>%
#
mutate(user_id = sample(!!total_users, n(), replace = TRUE)) %>%
setDT(key = "date") %>%
#
.[.[, .I[1L], by = user_id]$V1, amount := abs(amount)] %>%
# ,
#
#
unique(by = c("user_id", "date"))
Ătape 2. Calculez la date d'Ă©chĂ©ance de chaque prĂȘt
data.table
vous permet de changer les objets par rĂ©fĂ©rence mĂȘme Ă l'intĂ©rieur des fonctions, nous l'utiliserons activement.
#
accu_dt <- events_dt[amount < 0, .(accu = cumsum(amount), date), by = user_id]
ff <- function(dt){
#
#
accu_dt[dt, amount := i.amount, on = "user_id"]
accu_dt[is.na(amount) == FALSE, accu := accu + amount][accu > 0, accu := NA, by = user_id]
calc_dt <- accu_dt[!is.na(accu), head(date, 1), by = user_id]
# data.frame,
calc_dt[dt, on = "user_id"]$V1
}
repay_dt <- events_dt[amount > 0] %>%
.[, repayment_date := ff(.SD), by = date] %>%
.[order(user_id, date)]
Ătape 3. Calcul de la dynamique de la structure pour la pĂ©riode
calcDebt <- function(report_date){
as_tibble(repay_dt) %>%
# ,
filter(is.na(repayment_date) | repayment_date > !! report_date) %>%
mutate(delay = as.numeric(!!report_date - date)) %>%
#
mutate(tag = santoku::chop(delay, breaks = c(0, 31, 61, 90),
labels = c("< 0", "0-30", "31-60", "61-90", "90+"),
extend = TRUE, drop = FALSE)) %>%
#
group_by(tag) %>%
summarise(amount = sum(amount)) %>%
mutate_at("tag", as.character)
}
#
df <- seq.Date(as.Date("2021-04-01"), as.Date("2021-04-30"), by = "1 day") %>%
tibble(date = ., tbl = purrr::map(., calcDebt)) %>%
unnest(tbl)
#
ggplot(df, aes(date, amount, colour = tag)) +
geom_point(alpha = 0.5, size = 3) +
geom_line() +
ggthemes::scale_colour_tableau("Tableau 10") +
theme_minimal()
Nous pouvons obtenir quelque chose comme ça.
Un écran de code, au besoin.
Article précédent - "Storytelling R Report vs BI, A Pragmatic Approach . "