3
votes

Faire fondre la trame de données du panier en une seule trame de données sans boucles

J'ai un dataframe au format panier comme celui-ci:

dfSingle <- data.frame(product = character(),
                    transaction = integer())
for (row in 1:nrow(df))  {
  # Create a list of products
  productList <- unname(unlist(df[row, ]))

  # Remove blank spaces
  productList <- productList[!productList %in% ""]

  # Convert to a dataframe
  dfTemp <- as.data.frame(productList)
  colnames(dfTemp) <- "product"
  dfTemp$transaction <- row

  # Bind to larger dataframe with previous rows
  dfSingle <- rbind(dfSingle, dfTemp)

}

output:

  transaction   product
1           1   milk
2           1   eggs
3           1   water
4           2   beer
5           2   elbow grease
6           3   wrench
7           4   milk
8           4   beer

Ce que je voudrais produire est un dataframe dans un format unique comme celui-ci:

      V1      V2           V3
1   milk      eggs         water
2   beer      elbow grease      
3   wrench                   
4   milk      beer  

Pour l'instant, je veux les données dans un dataframe afin de pouvoir filtrer avant de passer au format de transactions utilisé par le package apriori R.

Quel est le moyen le plus rapide de convertir cette trame de données du panier au format unique?

En ce moment, j'utilise une boucle qui est très lente.

V1 <- c('milk', 'beer', 'wrench', 'milk' )
V2 <- c('eggs', 'elbow grease', '', 'beer')
V3 <- c('water', '', '', '')

df <- data.frame(V1, V2, V3)

J'ai pensé à utiliser apply pour appliquez cette fonction à chaque ligne, mais je ne sais pas comment lier les multiples lignes résultantes aux résultats des lignes précédentes.


0 commentaires

5 Réponses :


3
votes

Vous pouvez utiliser stack . L'astuce consiste à transposer votre bloc de données, c'est à dire

         values ind
1          milk   1
2          eggs   1
3         water   1
4          beer   2
5  elbow grease   2
7        wrench   3
10         milk   4
11         beer   4

NOTE: Un simple rgex ne peut extraire que les nombres de la colonne ind , ie

df1$ind <- gsub('\\D+', '', df1$ind)

qui donnera,

df1 <- stack(as.data.frame(t(df), stringsAsFactors = FALSE))

df1[df1$values != '',]
         values ind
#1          milk  V1
#2          eggs  V1
#3         water  V1
#4          beer  V2
#5  elbow grease  V2
#7        wrench  V3
#10         milk  V4
#11         beer  V4


0 commentaires

1
votes

Avec tidyverse vous pouvez faire:

df %>%
 mutate_all(funs(ifelse(. == "", NA_character_, paste0(.)))) %>%
 rowid_to_column(var = "transaction") %>%
 gather(var, product, -transaction, na.rm = TRUE) %>%
 select(-var) %>%
 arrange(transaction)

  transaction      product
1           1         milk
2           1         eggs
3           1        water
4           2         beer
5           2 elbow grease
6           3       wrench
7           4         milk
8           4         beer

Premièrement, il remplace les lignes vides par NA_character_. Deuxièmement, il crée une variable avec un ID de ligne appelée "transaction". Troisièmement, il transforme les données du format large au format long et supprime également les lignes avec NA_character_. Enfin, il organise les données selon "transaction".


0 commentaires

1
votes

ou une approche data.table (one-liner)

Premièrement, faites fondre la transaction à partir des noms de domaine: setDT (df) [ transaction: = .I] Puis fondre, en utilisant transaction comme colonne id: melt (..., id = "transaction") Et enfin, supprimez les valeurs vides et renvoyez la première et la troisième colonne: ... [! Value == "", c (1,3)]

melt( setDT(df)[, transaction := .I ], id = "transaction" )[!value == "", c(1,3) ]

#    transaction        value
# 1:           1         milk
# 2:           2         beer
# 3:           3       wrench
# 4:           4         milk
# 5:           1         eggs
# 6:           2 elbow grease
# 7:           4         beer
# 8:           1        water


1 commentaires

Accepter ceci comme réponse car il semble s'exécuter le plus rapidement en tant que table de données



1
votes

Après avoir remplacé le caractère "" par le format approprié NA , vous pouvez créer une nouvelle transaction de colonne puis utiliser reshape2 :: melt :

> out
  transaction        value
1           1         milk
5           1         eggs
9           1        water
2           2         beer
6           2 elbow grease
3           3       wrench
4           4         milk
8           4         beer

Ensuite:

out <- melted_df[order(melted_df$transaction), ]

ce qui donne:

> melted_df
  transaction variable        value
1           1       V1         milk
2           2       V1         beer
3           3       V1       wrench
4           4       V1         milk
5           1       V2         eggs
6           2       V2 elbow grease
8           4       V2         beer
9           1       V3        water

L'avantage de cette fonction est qu'elle vous donnera une colonne variable qui vous donnera le nom des colonnes du df data.frame précédent. Si cela ne vous concerne pas, supprimez cette colonne en utilisant df $ variable . Si vous souhaitez également trier le résultat par ordre croissant de transaction:

melted_df <- na.omit(reshape2::melt(data=df, id.vars="transaction"))

ce qui donne finalement:

df[df == ""]   <- NA    
df$transaction <- 1:nrow(df)


0 commentaires

0
votes

Une autre alternative de base R :

do.call(
  rbind, 
  sapply(seq_along(df), function(i) cbind(transaction = i, product = df[[i]][nzchar(df[[i]])])) 
)

     transaction product       
[1,] "1"         "milk"        
[2,] "1"         "beer"        
[3,] "1"         "wrench"      
[4,] "1"         "milk"        
[5,] "2"         "eggs"        
[6,] "2"         "elbow grease"
[7,] "2"         "beer"        
[8,] "3"         "water"  


0 commentaires