2016-06-29 14 views
2

Das Problem des Sammelns mehrerer Sätze von Spalten wurde bereits hier angesprochen: Gather multiple sets of columns, aber in meinem Fall sind die Spalten nicht eindeutig.Sammeln Sie doppelte Spaltensätze in einzelne Spalten

I haben folgende Daten:

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 

Die erste Spalte ist eine ID, dann habe ich viele wiederholten Spalten (der Original-Datensatz hat 133 Spalten):

  1. Identifikator für die Frage
  2. Punkte gegeben
  3. maximale Punktzahl

Ich möchte mit dieser Struktur, um am Ende:

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 

ich mehrere Dinge ausprobiert haben:

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

Beide haben die gewünschte Ausgabe nicht liefern. Darüber hinaus funktioniert gather mit mehr Spalten als hier gezeigt nicht mehr, da zu viele doppelte Spalten vorhanden sind.

Als Abhilfe habe ich versucht, dies:

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

, die einen Fehler gibt: Duplicate identifiers for rows (3, 9), (4, 10), (1, 7), (2, 8)

Dieses Problem bereits hier diskutiert wurde: Unexpected behavior with tidyr, aber ich weiß nicht, warum/wie ich ein anderes hinzufügen soll Kennung. Wahrscheinlich ist das nicht das Hauptproblem, denn ich sollte das Ganze wahrscheinlich anders angehen.

Wie könnte ich mein Problem lösen, vorzugsweise mit tidyr oder Basis? Ich weiß nicht, wie man data.table benutzt, aber falls es eine einfache Lösung gibt, werde ich mich auch damit abfinden.

+0

Sind alle Ihre Frage, Max_Points und Punkte Spalten eigentlich das gleiche Ding? –

+0

Vielleicht 'rbind (Eingabe [, c (1, 2: 4)], Eingabe [, c (1, 5: 7)])'? – zx8754

+0

@MikeyMike ja. –

Antwort

5

Versuchen Sie folgendes:

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

Sie müssen möglicherweise klären, wie Sie die ID-Spalte behandelt werden wollen, aber vielleicht so etwas wie das?

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

Nicht sicher, wie robust ist es in Bezug auf eine unterschiedliche Anzahl von wiederholten Spalten der Handhabung, aber es funktioniert für Ihre Testdaten und sollte funktionieren, wenn Sie Spalten die gleiche Anzahl von Malen wiederholt werden.

1

Ein anderer Weg, um das gleiche Ziel zu erreichen, ohne lapply mit:

Wir beginnen alle Spalten für Frage Grabben, max_points und Punkte dann schmelzen wir jede einzeln und cbind sie zusammen alle.

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

Der idiomatische Weg, dies in Daten zu tun.Tabelle ist ziemlich einfach:

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 

Sie erhalten die zusätzliche Spalte „Variable“ genannt, aber man kann mit res[, variable := NULL] danach, es loszuwerden, falls gewünscht.

Verwandte Themen