L'analyse des produits en pratique avec R

Problématique

La méta de gestion de produit moderne concerne la gestion axée sur les données.





Tout le monde veut une approche analytique et la capacité de prendre des décisions sur le tas de données produit disponibles. Mais en réalité, il y a un manque d'informations sur la manière exacte de procéder. Quels outils utiliser, comment prendre des décisions et comment explorer les données. Je voudrais partager précisément l’aspect pratique de cette question sous la forme d’une affaire.





Partie introductive

Début 2020, vous êtes un chef de produit ordinaire à qui on a proposé de développer un produit de prêt dans un autre pays. L'offre a été acceptée, les documents sont complétés, il est temps de se mettre au travail. 





La première chose qui me vient à l'esprit est de voir ce qui se passe avec l'économie du produit. Et comment le produit se comporte généralement.





Après quelques jours, une réunion où l'on vous demande de répondre à quelques questions:





  1. Maintenant, nous payons pour attirer un client 695494. Quel est le coût acceptable d'attirer pour nous? Est-il judicieux d'augmenter le coût par client pour obtenir plus de volume?





  2. Dans quelle mesure l'économie de portefeuille est-elle saine et quelle dynamique existe-t-il ici et maintenant?





  3. Nous avons récemment modifié l'approche de la taille de l'émission et avons commencé à émettre des chèques plus petits lors des premiers prêts. Comment cela a-t-il affecté le produit?





En général, des questions claires auxquelles tout propriétaire de produit devrait être en mesure de répondre. 





. , , : LTV, CAC . , , , .





. ( ), ( ) ( ).





, , . .





- , .





. , , . .





, , : R ,Rstudio,dbeaver( ). , .





, .   select * from transactions t .





Rows: 2,226,532
Columns: 10
$ borrower_id          2, 2, 2, 6, 6, 12, 12, 12, 12, 16, 20, 20, 20, 20, 22, 23, 23, 33, 33, 39, 39, 36, 36
$ con_id               1, 1, 1, 2, 2, 4, 4, 4, 4, 5, 7, 7, 7, 7, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, 13
$ disbursement_date    2017-11-23, 2017-11-23, 2017-11-23, 2017-11-24, 2017-11-24, 2017-11-27, 2017-11-27, …
$ prolongations_count  1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
$ loan_type            "pdl", "pdl", "pdl", "pdl", "pdl", "pdl", "pdl", "pdl", "pdl", "pdl", "pdl", "pdl", "…
$ date                 2017-12-15, 2017-11-23, 2017-12-06, 2017-11-24, 2017-12-25, 2017-12-29, 2017-11-27, …
$ type                 "Payments::Transaction::ContractAddTransaction", "Payments::Transaction::DisburseTran…
$ amount               250000, 1000000, 1200000, 2500000, 3500000, 1040000, 1500000, 10000, 1470000, 1500000
$ id                   325, 2, 127, 5, 587, 557500, 557499, 557504, 557507, 17, 182865, 182874, 182869, 1828
$ deleted_at           NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
      
      



. ( , - ).





, , . (disbursment_date). (prolongations_count) - . . "" .





- (ContractAdd- , DisburseTransaction - ).





. ( ): , -> -> -> .





: .





, .





( , 0.00003 ).





  • : , , , , .





  • R, , 2 . data table. tidyverse( )





.





. ( z_type am):





lk %>% data.table()->lk1 #    data table

lk1[,':='
(z_type=z_type<-fifelse(
#     -     
 type=='Payments::Transaction::ContractAddTransaction','add','disb'),
am=amount*fifelse(z_type=='add',1,-1))][1:20,c(-1,-11,-8,-6)] #  
lk1[disbursement_date<'2020-01-01' & date<='2020-03-01',c(-1,-11,-8,-6)]->lk1
glimpse(lk1) #   
      
      



$ borrower_id          2, 2, 2, 6, 6, 12, 12, 12, 12, 16, 20, 20, 20, 20, 22, 23, 23, 33, 33, 39, 39, 36, 36$ con_id               1, 1, 1, 2, 2, 4, 4, 4, 4, 5, 7, 7, 7, 7, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, 13$ disbursement_date    2017-11-23, 2017-11-23, 2017-11-23, 2017-11-24, 2017-11-24, 2017-11-27, 2017-11-27, …
$ prolongations_count  1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1$ date                 2017-12-15, 2017-11-23, 2017-12-06, 2017-11-24, 2017-12-25, 2017-12-29, 2017-11-27, …
$ amount               250000, 1000000, 1200000, 2500000, 3500000, 1040000, 1500000, 10000, 1470000, 1500000$ id                   325, 2, 127, 5, 587, 557500, 557499, 557504, 557507, 17, 182865, 182874, 182869, 1828$ z_type               "add", "disb", "add", "disb", "add", "add", "disb", "add", "add", "disb", "disb", "ad…
$ am                   250000, -1000000, 1200000, -2500000, 3500000, 1040000, -1500000, 10000, 1470000, -150…
      
      



, .





. , :





lk1[,.(total_in_mln=sum(am*for_ex)/1e6),.(z_type)]
      
      



total_in_mln





add





58.33176





disb





-45.49114





: (add) (disb). .





, :





lk1[!is.na(disbursement_date)&z_type=='disb',
.(sum=sum(am*for_ex*-1,na.rm = T)/1e6),
.(date=floor_date(disbursement_date,'month',))][,
ggplot(.SD,aes(date,sum,label=round(sum,2)))+
geom_col(fill=polar_night[2])+ff+tt+
  geom_text(aes(y=sum+0.1),col=aurora[1])+
  labs(x=' ',y='   ',
  title='   ')]
      
      



, . .





, .





- , . 





. , ( ) . 





, , :





lk1[][,':='( min_date=min(disbursement_date)),.(borrower_id)][,#     
c("gen",'dif'):=.(floor_date(min_date,'1 month'),as.numeric(date-min_date))][, #               
n:=uniqueN(borrower_id),][, #  
  .(sum=sum(am*for_ex),n=unique(n)),
  .(dif)
][order(dif)][,":="(bal=bal<-sum/n,cum=cumsum(bal))][,
    ggplot(.SD,aes(dif,cum))+ # 
   geom_line()+
   tt+ff+
        labs(x='  ',y='  USD',col='',
        title='LTV      ')+
   scale_y_continuous(breaks = seq(-200,200,20),
   labels =paste0('$',seq(-200,200,20),'k' ))+
   scale_x_continuous(breaks = seq(0,1000,20))  ]
      
      



LTV c . .





, , , . , , .





, 24 . ( ):





lk1[,':='( min_date=min(disbursement_date)),.(borrower_id)][,
c("gen",'dif'):=.(floor_date(min_date,'1 month'),as.numeric(date-min_date))][,
n:=uniqueN(borrower_id),.(gen)][,
  .(sum=sum(am*0.00003),n=unique(n)),
  .(gen,dif)#     
][order(gen,dif)][,
":="(bal=bal<-sum/n)][,':='(cum=cumsum(bal),m_dif=max(dif)),
.(gen)][dif<=m_dif-30 & gen %between% c('2018-01-01','2021-03-31')][,
 ggplot(.SD,aes(dif,cum,col=factor(gen)))+
 geom_line()+
 facet_wrap(~factor(year(gen),levels = c(2019,2018)),nrow=2)+tt+ff+
 labs(x='  ',y='  USD',col='',
 title = 'LTV      ')+
 scale_y_continuous(breaks = seq(-200,200,20),
 labels =paste0('$',seq(-200,200,20),'k' ))+
 scale_x_continuous(breaks = seq(0,1000,20))+
 geom_hline(yintercept = 695494*for_ex,color='red',size=1)+
 geom_hline(yintercept = 0,color='dark red',linetype='dashed')+
 geom_text(inherit.aes = F,aes(x=as.Date(600),
 y=695494*for_ex+3,group=1),label='   = $20.86',
 col='red',size=6)+tt+ff+
 theme(legend.text = element_text(size=20),
       legend.title = element_text(size=25))+
  guides(colour = guide_legend(override.aes = list(size=10)))]

      
      



- ( ), :





  1. , - .





  2. , .





  3. 0, .





  4. , , .









  1. . , (150 180).





  2. . , 2018 , . “” 10-40 . 2019 : .





  3. 40 60 . . , , 120-150 3





  4. 90 , .





,





1. 695494. ? , ?





- , - . - 40-60 . $20.8 (695494* 0.00003)





- , . - . 





- - . 





2. ?





, . - 100+ . 





.





3. . ? 





- . - .





Le travail du chef de produit est de prendre des décisions. 2,2 millions de lignes parlaient de ce qui se passait. Une telle analyse prend de 30 minutes à quelques heures, en fonction de la connaissance du domaine et de la saleté des données. Cette analyse ne nécessite rien de plus que des données brutes et des logiciels open source.





Plusieurs dizaines de lignes de code, un peu de bon sens et l'économie du produit est claire, calculée et des conclusions sont tirées.





Plusieurs évaluations et conclusions plus importantes sont facilement collectées à partir des mêmes données, mais à leur sujet en un temps d'arc.








All Articles