1
votes

Comment puis-je parcourir une fonction sur des colonnes spécifiques d'une série de dataframes où je peux définir l'ordre?

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?


0 commentaires

4 Réponses :


2
votes

É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")


0 commentaires

3
votes

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)


1 commentaires

Merci pour ce code. C'est en fait assez similaire à ce que j'ai actuellement, mais votre code est plus propre.



2
votes

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:

  1. regex-return-all-before-the-second -occurrence


0 commentaires

1
votes

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)))]


0 commentaires