2016-06-29 6 views
2

Le problème de la collecte de plusieurs ensembles de colonnes était déjà traité ici: Gather multiple sets of columns, mais dans mon cas, les colonnes ne sont pas uniques.Rassembler les ensembles de colonnes dupliqués en colonnes simples

I ont les valeurs suivantes:

input <- data.frame(
    id = 1:2, 
    question = c("a", "b"), 
    points = 0, 
    max_points = c(3, 5), 
    question = c("c", "d"), 
    points = c(0, 20), 
    max_points = c(5, 20), 
    check.names = F, 
    stringsAsFactors = F 
) 
input 
#> id question points max_points question points max_points 
#> 1 1  a  0   3  c  0   5 
#> 2 2  b  0   5  d  20   20 

La première colonne est un identifiant, j'ai plusieurs colonnes répétées (l'ensemble de données d'origine a 133 colonnes):

    identificateur
  1. pour question
  2. points donnés
  3. maximum de points

Je voudrais finir avec cette structure:

expected <- data.frame(
    id = c(1, 2, 1, 2), 
    question = letters[1:4], 
    points = c(0, 0, 0, 20), 
    max_points = c(3, 5, 5, 20), 
    stringsAsFactors = F 
) 
expected 
#> id question points max_points 
#> 1 1  a  0   3 
#> 2 2  b  0   5 
#> 3 1  c  0   5 
#> 4 2  d  20   20 

J'ai essayé plusieurs choses:

  • tidyr::gather(input, key, val, -id)
  • reshape2::melt(input, id.vars = "id")

Les deux ne fournissent pas la sortie désirée. De plus, avec plus de colonnes que celles montrées ici, gather ne fonctionne plus, car il y a trop de colonnes dupliquées.

Pour contourner ce problème, j'ai essayé ceci:

# add numbers to make col headers "unique" 
names(input) <- c("id", paste0(1:(length(names(input)) - 1), names(input)[-1])) 

# gather, remove number, spread 
input %>% 
    gather(key, val, -id) %>% 
    mutate(key = stringr::str_replace_all(key, "[:digit:]", "")) %>% 
    spread(key, val) 

qui donne une erreur: Duplicate identifiers for rows (3, 9), (4, 10), (1, 7), (2, 8)

Ce problème a déjà été discuté ici: Unexpected behavior with tidyr, mais je ne sais pas pourquoi/comment je devrais ajouter un autre identifiant. Très probablement ce n'est pas le problème principal, parce que je devrais probablement aborder l'ensemble différemment.

Comment pourrais-je résoudre mon problème, de préférence avec tidyr ou base? Je ne sais pas comment utiliser data.table, mais dans le cas où il y a une solution simple, je vais me contenter de cela aussi.

+0

Est-ce que toutes vos colonnes de questions, max_points et points sont nommées de la même façon? –

+0

Peut-être 'rbind (entrée [, c (1, 2: 4)], entrée [, c (1, 5: 7)])'? – zx8754

+0

@MikeyMike oui. –

Répondre

5

Essayez ceci:

do.call(rbind, 
     lapply(seq(2, ncol(input), 3), function(i){ 
      input[, c(1, i:(i + 2))] 
       }) 
     ) 

# id question points max_points 
# 1 1  a  0   3 
# 2 2  b  0   5 
# 3 1  c  0   5 
# 4 2  d  20   20 
0

Vous pourriez avoir besoin de clarifier la façon dont vous souhaitez que la colonne d'identification à traiter, mais peut-être quelque chose comme ça?

runme <- function(word , dat){ 
    grep(paste0("^" , word , "$") , names(dat)) 
} 

l <- mapply(runme , unique(names(input)) , list(input)) 
l2 <- as.data.frame(l) 

output <- data.frame() 
for (i in 1:nrow(l2)) output <- rbind(output , input[, as.numeric(l2[i,]) ]) 

Je ne sais pas comment il est robuste par rapport à la manipulation des nombres différents de colonnes répétées mais il fonctionne pour vos données de test et devrait fonctionner si vous colonnes sont répétées un nombre égal de fois.

1

Une autre façon d'atteindre le même objectif sans utiliser lapply:

Nous commençons par saisir toutes les colonnes pour la question, max_points, et les points nous fondent chacun individuellement et cbind tous ensemble.

library(reshape2) 

questions <- input[,c(1,c(1:length(names(input)))[names(input)=="question"])] 
points <- input[,c(1,c(1:length(names(input)))[names(input)=="points"])] 
max_points <- input[,c(1,c(1:length(names(input)))[names(input)=="max_points"])] 

questions_m <- melt(questions,id.vars=c("id"),value.name = "questions")[,c(1,3)] 
points_m <- melt(points,id.vars=c("id"),value.name = "points")[,3,drop=FALSE] 
max_points_m <- melt(max_points,id.vars=c("id"),value.name = "max_points")[,3, drop=FALSE] 

res <- cbind(questions_m,points_m, max_points_m) 
res 
    id questions points max_points 
1 1   a  0   3 
2 2   b  0   5 
3 1   c  0   5 
4 2   d  20   20 
4

La manière idiomatique de faire cela dans les données.table est assez simple:

library(data.table) 
setDT(input) 

res = melt(
    input, 
    id = "id", 
    meas = patterns("question", "^points$", "max_points"), 
    value.name = c("question", "points", "max_points") 
) 


    id variable question points max_points 
1: 1  1  a  0   3 
2: 2  1  b  0   5 
3: 1  2  c  0   5 
4: 2  2  d  20   20 

Vous obtenez la colonne supplémentaire appelée « variable », mais vous pouvez vous débarrasser de celui-ci avec res[, variable := NULL] ensuite si on le souhaite.