2016-05-11 2 views
3

Ich möchte eine Tabelle lesen und eine andere Tabelle erstellen, die zählt, wie oft eine eindeutige ID in mehreren spezifischen Spalten auftritt.Anzahl der Male, die ein Element in jeder der mehreren Spalten aufgetreten ist

Zum Beispiel habe ich eine Tabelle, wo jede Zeile eine Transaktion zeigt, mit einer userId die Rolle jeder Person identifiziert.

buyer  <- c("A", "A", "B", "A", "B", "C") 
seller  <- c("C", "B", "C", "B", "C", "A") 
negotiator <- c("B", "C", "D", "D", "A", "B") 

df <- data.frame(buyer, seller, negotiator) 
df 
# buyer seller negotiator 
# 1  A  C   B 
# 2  A  B   C 
# 3  B  C   D 
# 4  A  B   D 
# 5  B  C   A 
# 6  C  A   B 

Ich möchte dann eine Tabelle erstellen, die zählt, wie oft eine userId eine Rolle in den Transaktionen erfüllt.

# id asBuyer asSeller asNegotiator 
# A  3   1    1 
# B  2   2    2 
# C  1   3    1 
# D  0   0    2 

Müsste ich verschiedene Dataframes erstellen und dann zusammenführen?

Antwort

3

würde ich data.table verwenden:

Update inspiriert von MrFlick

library(data.table) 
setDT(df) 
dcast(melt(df, measure.vars = names(df)), value ~ variable) 
# value buyer seller negotiator 
# 1:  A  3  1   1 
# 2:  B  2  2   2 
# 3:  C  1  3   1 
# 4:  D  0  0   2 

Sie fun.aggregate = length als Argument an dcast hinzufügen könnte die Warnmeldung zu unterdrücken. Und Sie könnten value.name = "id" als Argument zu melt hinzufügen, wenn Sie diese Spalte mit dem Namen id heißen möchten.

Original, beantworten mehr

setDT(df) 

outDT <- data.table(id = unique(unlist(df))) 

invisible(
    sapply(names(df), function(jj) 
    outDT[df[ , .N, by = jj], 
      #set the name you desire by pasting; 
      # could use a regex or substr to 
      # for the first letter capital if need be 
      (jj2 <- paste0("as", jj)) := i.N, 
      #merge id to the count column 
      on = c(id = jj) 
      clean-up: missed observations were NA, set to 0 
      ][is.na(get(jj2)), (jj2) := 0]) 
) 
5

können Sie zunächst schmelzen Ihre Daten dann tabellarisch. Zum Beispiel

dd<-reshape2::melt(df,0) 
xtabs(~value+variable,dd) 
#  variable 
# value buyer seller negotiator 
#  A  3  1   1 
#  B  2  2   2 
#  C  1  3   1 
#  D  0  0   2 
+0

Wohin wird die 0 weitergeleitet? – MichaelChirico

+0

Interessant, ich habe nicht vorher an 'schmelzen (df, 0)' gedacht. :) Wenn man keine 'reshape2' und' df' Spalten hat, sind type Zeichen, vielleicht 'mit (setNames (stack (df), c (" id "," as ")), xtabs (~ id + as)) '. – lukeA

+0

@MichaelChirico Die 0 geht zum Parameter 'id.vars'. In diesem Fall gibt es keine Variable, die jede Zeile außer dem Zeilenindex selbst darstellt. – MrFlick

3

Hier ist eine Lösung, die nur Basis R mit (wahrscheinlich langsamer als andere Ansätze):

lst <- lapply(names(df), function(col) as.data.frame(table(df[[col]]),responseName=col)) 

mergeAll <- function(x,y) merge(x,y,all=TRUE) 

res <- Reduce(f=mergeAll, lst) 
names(res)[1] <- 'id' 
res[is.na(res)] <- 0 

> res 
    id buyer seller negotiator 
1 A  3  1   1 
2 B  2  2   2 
3 C  1  3   1 
4 D  0  0   2 
+3

etwas ähnliches Einzeiler: 'do.call (cbind, lapply (df, Funktion (L) Tabelle (Faktor (L, Ebenen (unlist (df)))))' –

+0

@MaratTalipov: tolle Idee, mit 'unlist (df) 'um alle möglichen Werte zu erzeugen, um' cbind' und nicht 'merge' zu ​​verwenden! +1 – digEmAll

+0

@MaratTalipov: Sie sollten Ihre eigene Antwort IMO;) – digEmAll

0

Es gibt zu viele R-Assistenten hier.

Hier ist meine einfache Lösung mit grundlegenden R mit nur ddply (Erstellen von "Count-Gruppe von" -Tabellen) und merge (Outer-Join durchführen).

# Create data frame for buyer count 
dfBuyer <- ddply(df, c("buyer"), summarise, count=length(seller)) 
colnames(dfBuyer) <- c("id", "asBuyer") 

dfBuyer 
# id asBuyer 
# 1 A  3 
# 2 B  2 
# 3 C  1 


# Create data frame for seller count 
dfSeller <- ddply(df, c("seller"), summarise, count=length(buyer)) 
colnames(dfSeller) <- c("id", "asSeller") 

dfSeller 
# id asSeller 
# 1 A  1 
# 2 B  2 
# 3 C  3 


# Create data frame for negotiator count 
dfNegotiator <- ddply(df, c("negotiator"), summarise, count=length(seller)) 
colnames(dfNegotiator) <- c("id", "asNegotiator") 

dfNegotiator 
# id asNegotiator 
# 1 A   1 
# 2 B   2 
# 3 C   1 
# 4 D   2 


# merge() apparently can merge only two dataframes at a time, 
# so to merge three dataframes, merge the first two and then 
# the third. Use "all=TRUE" to perform outer join. 

# Merge buyer and seller 
dfBuyerSellerMerged <- merge(x=dfBuyer, y=dfSeller, by="id", all=TRUE) 

# Merge buyer and seller and negotiator 
dfBuyerSellerNegotiatorMerged <- merge(x=dfBuyerSellerMerged, y=dfNegotiator, by="id", all=TRUE) 

dfBuyerSellerNegotiatorMerged 
# id asBuyer asSeller asNegotiator 
# 1 A  3  1   1 
# 2 B  2  2   2 
# 3 C  1  3   1 
# 4 D  NA  NA   2 


# Remove NAs. 
dfBuyerSellerNegotiatorMerged[is.na(dfBuyerSellerNegotiatorMerged)] <- 0 

dfBuyerSellerNegotiatorMerged 
# id asBuyer asSeller asNegotiator 
# 1 A  3  1   1 
# 2 B  2  2   2 
# 3 C  1  3   1 
# 4 D  0  0   2 
+1

Ihre Lösung ist in Ordnung, jedoch ist sie für Ihr spezielles Problem fest programmiert. Wenn Ihr Eingabedatenrahmen beispielsweise unterschiedliche Spaltennamen oder die Anzahl der Spalten hätte, müssten Sie Ihren Code neu schreiben. Ich würde vorschlagen, in die Lösung von @ digEmAll zu schauen, die dieselbe Logik wie Ihre Lösung hat, aber universeller ist.PS: 'base R' Code sollte keine zusätzlichen Pakete benötigen, während Ihr Code das Paket 'plyr' benötigt (wenn ich mich richtig erinnere). –

+2

Ja, wie @MaratTalipov richtig gesagt, das ist sehr ähnlich zu meiner Lösung; Der Hauptunterschied besteht darin, dass mein Code generischer ist und wenn Sie dem ursprünglichen data.frame eine neue Spalte hinzufügen, müssen Sie nichts ändern. In der Tat habe ich 'lapply' verwendet, um für jede Spalte zu zählen, und' Reduce', um merge mehrere Male aufzurufen. Sie haben auch 'ddply' von' plyr' Paket verwendet, also ist nicht streng Basis R. Wie auch immer, ich sehe kein besonderes Problem in Ihrem Ansatz;) – digEmAll

Verwandte Themen