2016-06-21 5 views
6

ich einen großen Datenrahmen haben, und ich möchte Strings in Spalten ausgerichtet werden, basierend auf Suffixe (Teil), sieht die Quelle Datenrahmen wie folgt aus:align Saiten einer Datenrahmen in Spalten in r

notSt für andere steht Variable preffixes

#   col1  col2  col3 
#  notst-s1 notst-s2 notst-x3 
#  notst-s1 notst-x3 notst-a5 
#  notst-s2 notst-a5 
#  notst-x3 notst-a5 

Das Ergebnis ignoriert werden sollte, sein:

#   col1  col2  col3  col4 
#  notst-s1 notst-s2 notst-x3 
#  notst-s1    notst-x3 notst-a5 
#     notst-s2    notst-a5 
#        notst-x3 notst-a5 

Edit: Betrachten Sie das ganze Suffix (nach "-"). Es hat keine Nummern. Es gibt Fälle, in denen die gesamte Zeichenfolge ("xxxx-spst") übereinstimmen muss (*), da der xxxx-Teil der Zeichenfolge in mehreren Versionen enthalten ist.

Für:

df <- read.table(text=" 
      col1   col2  col3 
     st1-ab  stb-spst sta-spst 
     stc-spst  sta-spst  st4-ab 
     stb-spst  st7-ab 
     st9-ba  stb-spst",header=TRUE,fill=TRUE,stringsAsFactors=FALSE) 

ein mögliches Ergebnis, könnten sein: (Spaltennamen und Reihenfolge irrelevant ist)

#   col1   col2  col3  col4  
#   st1-ab  stb-spst sta-spst  
#   st4-ab  stc-spst sta-spst   
#   st7-ab  stb-spst  
#     stb-spst     st9-ba  

(*), dass Hinweis in Zeile 2, Col2, „stc-SPST "scheint fehl am Platz, aber es ist kein Problem, weil der Wert stb-spst in dieser Zeile nicht existiert, so dass für diesen speziellen Fall nur das Suffix (" spst ") zählt. Mit anderen Worten, wenn die ganze Zeichenfolge (Präfix-Suffix) mit anderen (in anderen Zeilen) übereinstimmt, sollten sie in der gleichen Spalte sein, wenn nicht, wenn das Suffix mit dem Suffix (anderer Zeilen) übereinstimmt, sollten sie übereinstimmen Säule. Der resultierende Datenrahmen sollte die gleiche Anzahl von Zeilen wie die ursprüngliche und die niedrigste Anzahl von möglichen Spalten aufweisen.

BEARBEITEN. Antwort sollte universal sein und funktionieren für:

df2 <- read.table(text=" 
col1   col2  col3  col4 
st1-ab  stb-spst sta-spst std-spst 
stc-spst  sta-spst st4-ab  st2-ab 
stb-spst  st7-ab  sa-ac 
st9-ba  stb-spst",header=TRUE,fill=TRUE,stringsAsFactors=FALSE) 

zum Beispiel auch. Mögliches Ergebnis:

#   col1   col2  col3  col4 col5  col6  col7 
#   st1-ab  stb-spst sta-spst std-spst 
#   st4-ab  stc-spst sta-spst    st2-ab 
#   st7-ab  stb-spst          sa-ac 
#     stb-spst           st9-ba 

Beispiel 3

df3 <- read.table(text=" 
col1   col2  col3  col4 
st1-ab  stb-spst sta-spst std-spst 
stb-spst  sta-ab  
sta-spst  st7-ab  sa-ac 
sta-spst  stb-spst",header=TRUE,fill=TRUE,stringsAsFactors=FALSE) 

gewünschter Ausgang

col1 col2  col3  col4  col5 
1  st1-ab sta-spst stb-spst std-spst 
2  sta-ab    stb-spst   
3 sa-ac st7-ab sta-spst     
4     sta-spst stb-spst 

EDIT Beispiel 4. Um einfacher die Aufgabe zu machen, können Sie explizit in einer Funktion definieren die Suffixe das kann mehr als ein mögliches Präfix pro Reihe haben. In diesem Beispiel ("spst"). Jede Zeichenfolge mit einem anderen Suffix als "spst" sollte also nur ein mögliches Präfix pro Zeile haben und kann in der resultierenden df wie col2 in der gewünschten Ausgabe in eine Spalte reduziert werden. Dies ist nicht das, was ich ursprünglich wollte, weil ich mehr Spalten als erwartet bekommen werde. Idealerweise sollten Strings, die spst und verschiedene Präfixe enthalten, in den niedrigst möglichen Spalten erscheinen. Siehe oben).

df4 <- read.table(text=" 
col1   col2  col3  col4 
st1-ab  stb-spst sta-spst std-spst 
stb-spst  st1-ab  
sta-spst  st7-ab  sa-ac 
sta-spst  stb-spst st7-ab",header=TRUE,fill=TRUE,stringsAsFactors=FALSE) 

Ausgang

row_id col1 col2   col3  col4  col5 
1    st1-ab  sta-spst stb-spst std-spst 
2    st1-ab     stb-spst   
3  sa-ac st7-ab  sta-spst     
4    st7-ab  sta-spst stb-spst 
+0

Können Sie uns einige Logik sorgen dafür, wie die Daten um bewegt wird? Warum willst du das machen? –

+0

@Ferroao Die bearbeiteten neuen Beispieldaten und die erwartete Ausgabe für diesen ist verwirrend – akrun

+0

es hat Präfix und Suffix (getrennt durch -) wie zuvor. aber keine Zahlen im Suffix. Die Ausgabe basiert auf Suffixen und in einigen Fällen auf der ganzen Zeichenfolge, wenn mehrere Übereinstimmungen vorhanden sind (Spalten 2 und 3). – Ferroao

Antwort

1

mit vier Beispielen getestet, aber diese Version wurde ohne Rücksicht auf die Informationen erfolgt Sie haben als Workaround in Beispiel 4 hinzugefügt.

Der Mai Zusätzlich ist Shuffle-Logik (die ziemlich langsam sein kann), um die resultierende Datenrahmenform von rechts nach links zu komprimieren. Es ist möglich, dass die assigned_by_suffix und die assigned_by_single_suffix nicht mehr benötigt werden, aber ich habe nicht verifiziert.

Ausgänge sind am Ende des Codes

# examples 
df1 <- read.table(text=" 
col1   col2  col3 
st1-ab  stb-spst sta-spst 
stc-spst  sta-spst  st4-ab 
stb-spst  st7-ab 
st9-ba  stb-spst",header=TRUE,fill=TRUE,stringsAsFactors=FALSE) 

df2 <- read.table(text=" 
col1   col2  col3  col4 
st1-ab  stb-spst sta-spst std-spst 
stc-spst  sta-spst st4-ab  st2-ab 
stb-spst  st7-ab  sa-ac 
st9-ba  stb-spst",header=TRUE,fill=TRUE,stringsAsFactors=FALSE) 

df3 <- read.table(text=" 
col1   col2  col3  col4 
st1-ab  stb-spst sta-spst std-spst 
stb-spst  sta-ab  
sta-spst  st7-ab  sa-ac 
sta-spst  stb-spst",header=TRUE,fill=TRUE,stringsAsFactors=FALSE) 

df4 <- read.table(text=" 
col1   col2  col3  col4 
st1-ab  stb-spst sta-spst std-spst 
stb-spst  st1-ab  
sta-spst  st7-ab  sa-ac 
sta-spst  stb-spst st7-ab",header=TRUE,fill=TRUE,stringsAsFactors=FALSE) 

library(reshape2) 
library(tidyr) 
library(dplyr) 
library(stringr) 
library(assertthat) 

suffix <- function(s) {str_extract(s, "[^\\-]+$")} 

# make a tall dataframe with melt, and get the suffix 
dfm <- df4 %>% 
    mutate(row_id = seq_along(col1)) %>% 
    melt(id.vars="row_id") %>% 
    select(-2) %>% 
    filter(value != "") %>% 
    mutate(suffix = suffix(value)) %>% 
    arrange(value) 
assert_that(!any(duplicated(dfm[c("row_id", "value")]))) 

# initialize 
combined <- data.frame() 
remaining <- dfm 

# get the groups with more than 1 value 
matched_values <- dfm %>% 
    group_by(value, suffix) %>% 
    summarize(n=n()) %>% 
    filter(n>1) %>% 
    rename(group_id = value) %>% 
    ungroup() 

# .. and assign the group ids that match 
assigned_by_value <- remaining %>% 
    inner_join(matched_values %>% select(group_id), by = c("value" = "group_id")) %>% 
    mutate(group_id = value) %>% 
    select(row_id, value, suffix, group_id) 
combined <- combined %>% bind_rows(assigned_by_value) 
remaining <- dfm %>% anti_join(combined, by=c("row_id", "value")) 
# find the remaining suffixes 
matched_suffixes <- remaining %>% 
    group_by(suffix) %>% 
    summarize(n=n()) %>% 
    filter(n>1) %>% 
    select(-n) %>% 
    ungroup() 

# ... and assign those that match 
assigned_by_suffix <- remaining %>% 
    inner_join(matched_suffixes, by="suffix") %>% 
    mutate(group_id = suffix) 
combined <- bind_rows(combined, assigned_by_suffix) 
remaining <- remaining %>% anti_join(combined, by=c("row_id", "value")) 


# All that remain are singles assign matches by suffix, choosing the match with fewest 
assigned_by_single_suffix <- remaining %>% 
    inner_join(matched_values, by = "suffix") %>% 
    top_n(1, n) %>% 
    head(1) %>% 
    select(-n) 
combined <- bind_rows(combined, assigned_by_single_suffix) 
remaining <- remaining %>% anti_join(combined, by=c("row_id", "value")) 

# get the remaining unmatched 
unmatched <- remaining%>% 
    mutate(group_id = value) 
combined <- bind_rows(combined, unmatched) 
remaining <- remaining %>% anti_join(combined, by=c("row_id", "value")) 
assert_that(nrow(remaining) == 0) 

# any overloads (duplicates) need to bump to their own column 
dups <- duplicated(combined[,c("row_id", "group_id")]) 
combined$group_id[dups] <- combined$value[dups] 

assert_that(nrow(combined) == nrow(dfm)) 

# spread the result 

result <- spread(combined %>% select(-suffix), group_id, value, fill ="") 

# Shuffle any matching suffix from right to left, so l long as there 
# is corresponding space an that the whole column can move 
# i is source (startign from right) - j is target (starting from right) 
# 
drop_cols = c() 
suffixes <- suffix(names(result)) 
for (i in (ncol(result)):3) { 
    for(j in (i-1):2) { 
    if (suffixes[i] == suffixes[j]) { 
     non_empty <- which(result[,i] != "") # list of source to move 
     can_fill <- which(result[,j] == "") # list of targets can be filled 
     can_move <- all(non_empty %in% can_fill) # is to move a subset of can_fill? 

     # if there's space, shuffle the column down 
     if (can_move) { 
     # shuffle down 
     result[,j] <- if_else(result[,j] != "", result[,j], result[,i]) 
     drop_cols <- c(drop_cols, i) 
     result[,i] <- NA 
     break 
     } 
    }     
    } 
} 

if (!is.null(drop_cols)) { 
    result <- result[,-drop_cols] 
} 
result 

# Example 1 
# row_id  ab st9-ba sta-spst stb-spst 
# 1  1 st1-ab  sta-spst stb-spst 
# 2  2 st4-ab  sta-spst stc-spst 
# 3  3 st7-ab     stb-spst 
# 4  4  st9-ba   stb-spst 

# Example 2 
# row_id  ab sa-ac  spst st2-ab st9-ba sta-spst stb-spst 
# 1  1 st1-ab  std-spst    sta-spst stb-spst 
# 2  2 st4-ab  stc-spst st2-ab  sta-spst   
# 3  3 st7-ab sa-ac         stb-spst 
# 4  4        st9-ba   stb-spst 

# Example 3 
# row_id  ab sa-ac sta-spst stb-spst std-spst 
# 1  1 st1-ab  sta-spst stb-spst std-spst 
# 2  2 sta-ab    stb-spst   
# 3  3 st7-ab sa-ac sta-spst     
# 4  4    sta-spst stb-spst 

# Example 4 
# row_id sa-ac st1-ab sta-spst stb-spst std-spst 
# 1  1  st1-ab sta-spst stb-spst std-spst 
# 2  2  st1-ab   stb-spst   
# 3  3 sa-ac st7-ab sta-spst     
# 4  4  st7-ab sta-spst stb-spst   
> 
+0

Ich sehe das Problem und habe einiges vom Ziel falsch verstanden. Ich werde einen letzten Versuch unternehmen – epi99

4

gewünschten wir dies durch erste melt ing die Datenmenge tun, den numerischen Index aus den Elementen extrahieren, eine Zeilen/Spalten-Index auf, dass, und weisen die Elemente zu einem auf Basis erstellen matrix erstellt basierend auf dem Maximalwert des Indexes.

library(reshape2) 
d1 <- na.omit(transform(melt(as.matrix(df1)), v1 = as.numeric(sub("\\D+", "", value)))) 
m1 <- matrix("", nrow = max(d1$Var1), ncol = max(d1$v1)) 
m1[as.matrix(d1[c("Var1", "v1")])] <- as.character(d1$value) 
d2 <- as.data.frame(m1[,!!colSums(m1!="")]) 
colnames(d2) <- paste0("col", seq_along(d2)) 
d2 
#  col1  col2  col3  col4 
#1 notst-s1 notst-s2 notst-x3   
#2 notst-s1   notst-x3 notst-a5 
#3   notst-s2   notst-a5 
#4     notst-x3 notst-a5 
+0

Dies funktioniert, wenn Zahlen wie im Beispiel vor der Bearbeitung haben. Dies funktioniert jedoch nicht, wenn die Zeichenfolgen die Nummer teilen, z. -s1 und -x1 sollten sich ebenfalls in verschiedenen Spalten befinden. Allgemein gesprochen sollte auch Text ohne Zahlen (wie in der Bearbeitung) berücksichtigt werden. – Ferroao

2

Matrix Indizierung könnte dies eine Möglichkeit Gebrauch machen:

sel <- dat!="" 
unq <- unique(dat[sel]) 
mat <- matrix(NA, nrow=nrow(dat), ncol=length(unq)) 

mat[cbind(row(dat)[sel], match(dat[sel], unq))] <- dat[sel] 

#  [,1]  [,2]  [,3]  [,4]  
#[1,] "notst-s1" "notst-s2" "notst-x3" NA   
#[2,] "notst-s1" NA   "notst-x3" "notst-a5" 
#[3,] NA   "notst-s2" NA   "notst-a5" 
#[4,] NA   NA   "notst-x3" "notst-a5" 

Wo dat als importiert wurde:

dat <- read.table(text=" 
    col1  col2  col3 
notst-s1 notst-s2 notst-x3 
notst-s1 notst-x3 notst-a5 
notst-s2 notst-a5 
notst-x3 notst-a5",header=TRUE,fill=TRUE,stringsAsFactors=FALSE) 
+0

Danke thelatemail, aber Ihre Antwort berücksichtigt nicht, dass "Notst" -String variieren kann, die Antwort von akrun geht davon aus. – Ferroao

Verwandte Themen