4
votes

Comment effectuer plusieurs opérations par ligne avec dépendance avec les lignes précédentes en utilisant [r] data.table (si possible)

J'ai le tableau de données suivant:

while(any(is.na(dt))){
  dt[, `:=` (
    EO_3 = calc_EO_3(EO_1, EO_2),
    EO_1 = ifelse(ID == "ID_001", EO_1, calc_EO_1(EO_1, EO_2)),
    EO_2 = ifelse(ID == "ID_001", EO_2, calc_EO_2(EO_1, EO_2, EO_3))
  )]  
}

et j'essaie d'effectuer des opérations par ligne, qui dépendent parfois des données des lignes précédentes. Plus précisément:

  ID   |   EO_1      |     EO_2      |       EO_3      | GROUP
ID_001 |  0.50000000 |   1.20000000  |      0.60000000 |   A  
ID_002 |  0.60000000 |   0.43200000  |      0.25920000 |   A
ID_003 |  0.25920000 |   0.02902376  |      0.00752296 |   A
ID_004 |  0.00752296 |   0.00000164  |      0.00000001 |   A
ID_001 |  0.40000000 |   2.50000000  |      1.00000000 |   B
ID_002 |  1.00000000 |   2.50000000  |      2.50000000 |   B
ID_003 |  2.50000000 |  15.62500000  |     39.06250000 |   B
ID_004 | 39.06250000 | 23841.8580000 | 931322.57810000 |   B   

Le dernier devrait être calculé à partir de la première ligne car il dépend des autres champs (cela devrait être facile) et, après cela, des trois les opérations devraient avoir lieu consécutivement et par ligne.

Le plus proche que j'ai été a été le suivant:

  ID   | EO_1 | EO_2 | EO_3 | GROUP
ID_001 | 0.5  |  1.2 |  0.6 |   A  
ID_002 |      |      |      |   A
ID_003 |      |      |      |   A
ID_004 |      |      |      |   A
ID_001 | 0.4  |  2.5 |  1.0 |   B
ID_002 |      |      |      |   B
ID_003 |      |      |      |   B
ID_004 |      |      |      |   B  

mais il ne calcule que le première ligne correctement:

first_row_bygroup_index <- dt[, .I[1], by = GROUP]$V1

dt[first_row_bygroup_index, 
   EO_3 := calc_EO_3(EO_1, EO_2)
     ]

dt[!first_row_bygroup_index, 
   `:=` (
     EO_1 = calc_EO_1(EO_1, EO_2),
     EO_2 = calc_EO_2(EO_1, EO_2, EO_3),
     EO_3 = calc_EO_3(EO_1, EO_2)
     ),
   by = row.names(dt[!first_row_bygroup_index])]

Étant ces espaces NA.

Je ne pense pas que je suis trop loin de la solution, mais je suis pas en mesure de trouver un moyen de le faire fonctionner. Le problème est que je ne peux pas effectuer d'opérations dans des sous-ensembles de lignes en utilisant des lignes extérieures au sous-ensemble.

MODIFIER J'ai manqué le résultat attendu:

calc_EO_1 <- function(
  EO_1,
  EO_2
){
  EO_1 <- shift(EO_1, type = "lag") * shift(EO_2, type = "lag")
  return(EO_1)
}

calc_EO_2 <- function(
  EO_1,
  EO_2,
  EO_3
){
  EO_2 <- EO_1 * shift(EO_2, type = "lag") * shift(EO_3, type = "lag")
  return(EO_2)
}

calc_EO_3 <- function(
  EO_1,
  EO_2
){
  EO_3 <- EO_1 * EO_2
  return(EO_3)
}

NOUVELLE MODIFICATION J'ai trouvé l'extrait de code suivant, mais je préfère attendre un peu pour voir si quelqu'un peut trouver une solution plus efficace que celle-ci:

dt <- fread("
  ID   | EO_1 | EO_2 | EO_3 | GROUP
ID_001 | 0.5  |  1.2 |      |   A  
ID_002 |      |      |      |   A
ID_003 |      |      |      |   A
ID_004 |      |      |      |   A
ID_001 | 0.4  |  2.5 |      |   B
ID_002 |      |      |      |   B
ID_003 |      |      |      |   B
ID_004 |      |      |      |   B  
            ", 
            sep = "|",
            colClasses = c("character", "numeric", "numeric", "numeric", "character"))

J'ai trouvé une solution de dplyr similaire, avec ce correctif laid while-loop également. La clé serait de trouver un moyen de faire un calcul par ligne qui pourrait obtenir des informations de la ligne précédente, même si cette ligne avant serait en dehors du sous-ensemble sélectionné. J'espère que quelqu'un pourra améliorer cela, alors j'attendrai un peu avant de le marquer comme solution.


0 commentaires

3 Réponses :


1
votes

S'agit-il du type de données auquel vous vous attendez du produit final?

go <- function(x, y, n) {
  z <- x * y
  for (i in 1:(n - 1)) {
    x <- c(x[1] * y[1], x)
    y <- c(x[1] * y[1] * z[1], y)
    z <- x * y
  }
  data.table(EO_1 = x, EO_2 = y, EO_3 = z)[.N:1][, lapply(.SD, round, 8)]
}

go(.5, 1.2, 4)

         EO_1       EO_2       EO_3
1: 0.50000000 1.20000000 0.60000000
2: 0.60000000 0.43200000 0.25920000
3: 0.25920000 0.02902376 0.00752296
4: 0.00752296 0.00000164 0.00000001


2 commentaires

Oh oui. Mon erreur, je vais mettre à jour la question avec le résultat attendu. Ne vous inquiétez pas des valeurs réelles, il s'agit d'un cas simplifié avec des entrées aléatoires, la vraie chose est bien plus grande, comme d'habitude. Comment pourrais-je calculer le tout à partir de l'entrée donnée?


J'ai trouvé une solution, même si elle est inefficace et laide. Pensez-vous pouvoir l'améliorer?



2
votes

Voici une autre approche possible:

       ID        EO_1         EO_2         EO_3 GROUP
1: ID_001  0.50000000 1.200000e+00 6.000000e-01     A
2: ID_002  0.60000000 4.320000e-01 2.592000e-01     A
3: ID_003  0.25920000 2.902376e-02 7.522960e-03     A
4: ID_004  0.00752296 1.642598e-06 1.235720e-08     A
5: ID_001  0.40000000 2.500000e+00 1.000000e+00     B
6: ID_002  1.00000000 2.500000e+00 2.500000e+00     B
7: ID_003  2.50000000 1.562500e+01 3.906250e+01     B
8: ID_004 39.06250000 2.384186e+04 9.313226e+05     B

sortie:

dt[!is.na(EO_1), EO_3 := EO_1 * EO_2, by=.(GROUP)]
dt[ID!="ID_001", c("EO_1", "EO_2", "EO_3") :=
    dt[,
        {
            eo1 <- EO_1[1L]; eo2 <- EO_2[1L]; eo3 <- EO_3[1L]
            .SD[ID!="ID_001",
                {
                    eo1 <- eo1 * eo2
                    eo2 <- eo1 * eo2 * eo3
                    eo3 <- eo1 * eo2
                    .(eo1, eo2, eo3)
                },
                by=.(ID)]
        },
        by=.(GROUP)][, -1L:-2L]
]


0 commentaires

1
votes

Question délicate! J'ai essayé d'utiliser nest de dplyr et d'appliquer une fonction de costum.

      ID        EO_1           EO_2            EO_3 GROUP
1 ID_001  0.50000000     1.20000000      0.60000000     A
2 ID_002  0.60000000     0.43200000      0.25920000     A
3 ID_003  0.25920000     0.02902376      0.00752296     A
4 ID_004  0.00752296     0.00000164      0.00000001     A
5 ID_001  0.40000000     2.50000000      1.00000000     B
6 ID_002  1.00000000     2.50000000      2.50000000     B
7 ID_003  2.50000000    15.62500000     39.06250000     B
8 ID_004 39.06250000 23841.85791016 931322.57461548     B

vous donnant

options("scipen"=999, "digits"=8)
library(tidyverse)

# Custom function
logic <- function(.df){
  for(i in 2:nrow(.df)){
    .df[i, "EO_1"] <- .df[i-1, "EO_1"] * .df[i-1, "EO_2"]
    .df[i, "EO_2"] <- .df[i, "EO_1"] * .df[i-1, "EO_2"] * .df[i-1, "EO_3"]
    .df[i, "EO_3"] <- .df[i, "EO_1"] * .df[i, "EO_2"]
  }
  .df
}

# Answers the question
dt <- dt %>% 
  mutate(EO_3 = EO_1 * EO_2) %>% 
  nest(-GROUP) %>% 
  mutate(data = map(data, ~logic(.))) %>% 
  unnest()

# Fixing nice output
dt %>% 
  mutate_at(vars(contains("EO_")), ~round(., 8)) %>% 
  select(-GROUP, everything(), GROUP) %>% 
  as.data.frame()


0 commentaires