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.
5 Réponses :
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
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".
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
Accepter ceci comme réponse car il semble s'exécuter le plus rapidement en tant que table de données
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)
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"