Je travaille pour une compagnie d'assurance et j'essaie d'améliorer quelque chose que j'ai construit. J'ai environ 150 cadres de données qui ressemblent à ceci:
step2_answer<-cbind(dt_Premium[,1],dt_Premium[,2:4]* dt_Discount_Factors[,2:4])
Je prends la prime de base, puis je multiplie par facteurs, puis j'ajoute une dépense fixe au plus finir. Mon code est actuellement quelque chose comme:
dt_Final_Premium<-cbind(dt_Premium[,1],dt_Premium[,2:4]* dt_Discount_Factors[,2:4]* dt_Territory_Factors[,2:4]+ dt_Fixed_Expense[,2:4])
Ce que je déteste à ce sujet:
-Le truc 2: 4 (j'aimerais pouvoir utiliser une plage nommée)
-Le typage est monstrueux compte tenu de toutes les tables et politiques que j'ai réellement
-Il est très déroutant pour quiconque sauf moi (l'auteur) de comprendre et de modifier / ajustez le code
-Je voudrais pouvoir avoir chaque étape de notation dans le cadre d'une liste, puis itérer simplement sur cette liste (ou un processus similaire).
-Idéalement, je pourrais obtenir les valeurs à chaque étape. Par exemple:
library(data.table) dt_Premium<-data.table(Policy = c("Pol123","Pol333","Pol555","Pol999"), Base_Premium_Fire= c(45,55,105,92), Base_Premium_Water= c(20,21,24,29), Base_Premium_Theft= c(3,5,6,7)) dt_Discount_Factors<-data.table(Policy = c("Pol123","Pol333","Pol555","Pol999"), Discount_Factor_Fire= c(.9,.95,.99,.97), Discount_Factor_Water= c(.8,.85,.9,.96), Discount_Factor_Theft= c(1,1,1,1)) dt_Territory_Factors<-data.table(Policy = c("Pol123","Pol333","Pol555","Pol999"), Territory_Factor_Fire= c(1.9,1.2,.91,1.03), Territory_Factor_Water= c(1.03,1.3,1.25,1.01), Territory_Factor_Theft= c(1,1.5,1,.5)) dt_Fixed_Expense<-data.table(Policy = c("Pol123","Pol333","Pol555","Pol999"), Fixed_Expense_Fire= c(5,5,5,5), Fixed_Expense_Water= c(7,7,7,7), Fixed_Expense_Theft= c(9,9,9,9))
Il doit juste y avoir un moyen pour que je puisse prendre une dataframe / datatable et ensuite simplement multiplier ou ajouter à la dataframe / datatable suivante dans la série. Merci d'avoir regardé ceci?
4 Réponses :
Étant donné que vos colonnes ont un nom propre, un certain pivotement peut faire le travail:
list(dt_Premium, dt_Discount_Factors, dt_Territory_Factors, dt_Fixed_Expense) %>% reduce(left_join, by='Policy') %>% pivot_longer(cols=-Policy)%>% separate(name, into=c("name", "object"), sep="_.*_") %>% pivot_wider() %>% mutate(total=Base*Discount*Territory+Fixed) %>% #of calculate the value for a specific step select(Policy, object, total) %>% pivot_wider(names_from = "object", values_from = "total")
Après avoir joint toutes les colonnes, vous pouvez pivoter vers un format long et transformer les colonnes en lignes. Là, vous pouvez séparer le nom en le vrai nom (Base, Remise, Fixe ...) et l'objet (Feu, Eau, ...) et revenir au format large. La partie la plus délicate est d'obtenir une bonne expression régulière, car vos noms utilisent deux fois le trait de soulignement. Le mien peut être considérablement amélioré mais fera le travail pour le moment.
Après cela, vous pouvez calculer ce que vous voulez, ne sélectionner que le résultat et pivoter en large une dernière fois. Si vous voulez obtenir tous les résultats, vous pouvez modifier ce dernier pivot avec des préfixes.
Le pivotement est une véritable gymnastique, mais il s'est avéré très efficace une fois que vous vous y êtes habitué.
Comme vous avez beaucoup de tables, si vous pouvez les obtenir sous forme de liste, vous pouvez également utiliser purrr :: reduction
pour les joindre toutes en même temps et simplifier les premières lignes de code:
library(tidyverse) #to be run after library(data.table) dt_Premium %>% left_join(dt_Discount_Factors, by="Policy") %>% left_join(dt_Territory_Factors, by="Policy") %>% left_join(dt_Fixed_Expense, by="Policy") %>% pivot_longer(cols=-Policy)%>% separate(name, into=c("name", "object"), sep="_.*_") %>% pivot_wider() %>% mutate(total=Base*Discount*Territory+Fixed) %>% #or calculate the value for a specific step select(Policy, object, total) %>% pivot_wider(names_from = "object", values_from = "total")
Que diriez-vous de quelque chose comme ça avec dplyr?! Ici, j'utilise le même calcul que vous avez mentionné, mais en utilisant la fonction mutate de dplyr, ce qui permet de voir l'étape par étape et à tout le monde de comprendre facilement le calcul.
library(data.table) library(dplyr) dt_Premium <- data.table(Policy = c("Pol123","Pol333","Pol555","Pol999"), Base_Premium_Fire= c(45,55,105,92), Base_Premium_Water= c(20,21,24,29), Base_Premium_Theft= c(3,5,6,7)) dt_Discount_Factors <- data.table(Policy = c("Pol123","Pol333","Pol555","Pol999"), Discount_Factor_Fire= c(.9,.95,.99,.97), Discount_Factor_Water= c(.8,.85,.9,.96), Discount_Factor_Theft= c(1,1,1,1)) dt_Territory_Factors <- data.table(Policy = c("Pol123","Pol333","Pol555","Pol999"), Territory_Factor_Fire= c(1.9,1.2,.91,1.03), Territory_Factor_Water= c(1.03,1.3,1.25,1.01), Territory_Factor_Theft= c(1,1.5,1,.5)) dt_Fixed_Expense <- data.table(Policy = c("Pol123","Pol333","Pol555","Pol999"), Fixed_Expense_Fire= c(5,5,5,5), Fixed_Expense_Water= c(7,7,7,7), Fixed_Expense_Theft= c(9,9,9,9)) dt_Final_Premium <- cbind(dt_Premium[,1],dt_Premium[,2:4]* dt_Discount_Factors[,2:4]* dt_Territory_Factors[,2:4]+ dt_Fixed_Expense[,2:4]) new_dt_final_premium <- dt_Premium %>% # Joining all tables together left_join(dt_Discount_Factors, by = "Policy") %>% left_join(dt_Territory_Factors, by = "Policy") %>% left_join(dt_Fixed_Expense, by = "Policy") %>% # Calculating required calculation mutate( Base_Premium_Fire = Base_Premium_Fire * Discount_Factor_Fire * Territory_Factor_Fire + Fixed_Expense_Fire, Base_Premium_Water = Base_Premium_Water * Discount_Factor_Water * Territory_Factor_Water + Fixed_Expense_Water, Base_Premium_Theft = Base_Premium_Theft * Discount_Factor_Theft * Territory_Factor_Theft + Fixed_Expense_Theft) %>% select(Policy, Base_Premium_Fire, Base_Premium_Water, Base_Premium_Theft)
Merci pour ce code. C'est en fait assez similaire à ce que j'ai actuellement, mais votre code est plus propre.
Une autre option consiste à réorganiser les données en les convertissant dans un format long, à les fusionner puis à effectuer les calculs:
dtLs <- list(dt_Premium, dt_Discount_Factors, dt_Territory_Factors, dt_Fixed_Expense)
sortie:
Policy variable Base_Premium Discount_Factor Territory_Factor Fixed_Expense disc_prem disc_prem_loc Final_Premium 1: Pol123 Fire 45 0.90 1.90 5 40.50 76.9500 81.9500 2: Pol123 Theft 3 1.00 1.00 9 3.00 3.0000 12.0000 3: Pol123 Water 20 0.80 1.03 7 16.00 16.4800 23.4800 4: Pol333 Fire 55 0.95 1.20 5 52.25 62.7000 67.7000 5: Pol333 Theft 5 1.00 1.50 9 5.00 7.5000 16.5000 6: Pol333 Water 21 0.85 1.30 7 17.85 23.2050 30.2050 7: Pol555 Fire 105 0.99 0.91 5 103.95 94.5945 99.5945 8: Pol555 Theft 6 1.00 1.00 9 6.00 6.0000 15.0000 9: Pol555 Water 24 0.90 1.25 7 21.60 27.0000 34.0000 10: Pol999 Fire 92 0.97 1.03 5 89.24 91.9172 96.9172 11: Pol999 Theft 7 1.00 0.50 9 7.00 3.5000 12.5000 12: Pol999 Water 29 0.96 1.01 7 27.84 28.1184 35.1184
data:
DT <- Reduce(merge, lapply(dtList, function(d) { vn <- sub('_([^_]*)$', '', names(d)[2L]) #see reference [1] melt(d, id.vars="Policy", value.name=vn)[, variable := gsub("(.*)_(.*)_(.*)", "\\3", variable)] })) DT DT[, disc_prem := Base_Premium * Discount_Factor][, disc_prem_loc := disc_prem * Territory_Factor][, Final_Premium := disc_prem_loc + Fixed_Expense]
Référence:
Je suppose que la lecture de quelques vignettes de rdata.table vous aiderait à resserrer la syntaxe et à la rendre plus laconique. Certains d'entre nous pensent laconique = «plus lisible» dans la programmation numérique. D'autres pensent que cela représente un certain niveau de folie:
seqXpi <- function(x) {x * pi} seqXexp <- function(x) {x * exp(1)} l <- {}; for(x in seq(1,10,1)) l <- as.data.table(rbind(l,cbind(seq=x,seqXpi=seqXpi(x),seqXexp=seqXexp(x))))
Comprendre Map, Reduce, mget et d'autres notations fonctionnelles dans R et rdata.table peut aider. Voici certaines choses que j'ai faites à partir d'un état d'esprit data.table:
Supprimer la syntaxe de cols peut être plus laconique en utilisant 'i' pour supprimer un vecteur de cols:
nm1 <- names(dt1)[1:4] nm2 <- names(dt2)[1:4] dt[, SumCol := Reduce(`+`, Map(`*`, mget(nm1), mget(nm2)))]