2012-10-22 7 views
3

Je me suis gratté la tête par-dessus. J'ai deux trames de données: dfmultiplier les colonnes de trames de données

df <- data.frame(group = 1:3, 
       age = seq(30, 50, length.out = 3), 
       income = seq(100, 500, length.out = 3), 
       assets = seq(500, 800, length.out = 3)) 

et weights

weights <- data.frame(age = 5, income = 10) 

Je voudrais multiplier ces deux trames de données uniquement pour les mêmes noms de colonnes. J'ai essayé quelque chose comme ceci:

colwise(function(x) {x * weights[names(x)]})(df) 

mais que de toute évidence n'a pas fonctionné comme colwise ne garde pas le nom de colonne dans la fonction. J'ai regardé diverses solutions (example), mais je suis incapable de trouver une réponse.

Le résultat data.frame devrait ressembler à ceci:

structure(list(group = 1:3, age = c(150, 200, 250), income = c(1000, 
3000, 5000), assets = c(500, 650, 800)), .Names = c("group", 
"age", "income", "assets"), row.names = c(NA, -3L), class = "data.frame") 

    group age income assets 
1  1 150 1000 500 
2  2 200 3000 650 
3  3 250 5000 800 

Répondre

6

sweep() est votre ami ici, pour cet exemple particulier. Il repose sur les noms dans df et weights étant dans le bon ordre, mais cela peut être arrangé.

> nams <- names(weights) 
> df[, nams] <- sweep(df[, nams], 2, unlist(weights), "*") 
> df 
    group age income assets 
1  1 150 1000 500 
2  2 200 3000 650 
3  3 250 5000 800 

Si les noms de variables dans weights et df ne sont pas dans le même ordre, vous pouvez les rendre si:

> df2 <- data.frame(group = 1:3, 
+     age = seq(30, 50, length.out = 3), 
+     income = seq(100, 500, length.out = 3), 
+     assets = seq(500, 800, length.out = 3)) 
> nams <- c("age", "income") ## order in df2 
> weights2 <- weights[, rev(nams)] 
> weights2 ## wrong order compared to df2 
    income age 
1  10 5 
> df2[, nams] <- sweep(df2[, nams], 2, unlist(weights2[, nams]), "*") 
> df2 
    group age income assets 
1  1 150 1000 500 
2  2 200 3000 650 
3  3 250 5000 800 

En d'autres termes, nous réordonner tous les objets de sorte que age et income sont en dans le bon ordre.

+0

merci @Gavin. (J'ai regardé balayage avant de poster ceci, mais n'ai pas compris la fonction du tout). Pourriez-vous préciser comment ne pas compter sur l'ordre des noms? La df originale pourrait avoir 100s de colonnes, mais les poids pourraient être seulement peu et dans n'importe quel ordre. – karlos

+0

Voir ma mise à jour. La clé est de s'assurer que chaque objet est dans le bon ordre pendant la ligne de code. Réglez donc l'ordre que vous voulez dans 'nams', puis commandez tous les objets par' nams'. –

+0

merci! Je pense que c'est une excellente solution et pourrait fonctionner dans de nombreux cas. – karlos

3

Quelqu'un pourrait avoir un moyen habile de le faire avec plyr, mais cela est sans doute la voie à suivre plus directement dans la base R.

shared.names <- intersect(names(df), names(weights)) 
cols <- sapply(names(df), USE.NAMES=TRUE, simplify=FALSE, FUN=function(name) 
     if (name %in% shared.names) df[[name]] * weights[[name]] else df[[name]]) 
data.frame(do.call(cbind, cols)) 

# group age income assets 
# 1  1 150 1000 500 
# 2  2 200 3000 650 
# 3  3 250 5000 800 
+0

On dirait que nous étions en train de penser selon les mêmes lignes générales.J'oublie toujours 'intersect()'. – A5C1D2H2I1M1N2O1R2T1

3

Vos données:

df <- data.frame(group = 1:3, 
       age = seq(30, 50, length.out = 3), 
       income = seq(100, 500, length.out = 3), 
       assets = seq(500, 800, length.out = 3)) 
weights <- data.frame(age = 5, income = 10) 

La logique:

# Basic name matching looks like this 
names(df[names(df) %in% names(weights)]) 
# [1] "age" "income" 

# Use that in `sapply()` 
sapply(names(df[names(df) %in% names(weights)]), 
     function(x) df[[x]] * weights[[x]]) 
#  age income 
# [1,] 150 1000 
# [2,] 200 3000 
# [3,] 250 5000 

La mise en œuvre:

# Put it all together, replacing the original data 
df[names(df) %in% names(weights)] <- sapply(names(df[names(df) %in% names(weights)]), 
              function(x) df[[x]] * weights[[x]]) 

Le résultat:

df 
# group age income assets 
# 1  1 150 1000 500 
# 2  2 200 3000 650 
# 3  3 250 5000 800 
+0

+1, plus simple encore –

+0

merci @mrdwab. c'est une solution très simple mais efficace – karlos

0

Vous pouvez aussi le faire dans une boucle en utilisant un indice résultant dont (% en%). L'approche ci-dessus est beaucoup plus efficace mais c'est une alternative.

results <- list() 
    for (i in 1:length(which(names(df) %in% names(weights)))) { 
    idx1 <- which(names(df) %in% names(weights))[i] 
     idx2 <- which(names(weights) %in% names(df))[i] 
    results[[i]] <- dat[,idx1] * weights[idx2] 
    } 
unlist(results) 
2

Voici une solution data.table

library(data.table) 
DT <- data.table(df) 
W <- data.table(weights) 

Utilisez mapply (ou Map) pour calculer les nouvelles colonnes et ajouter ensuite les deux à la fois par référence.

DT <- data.table(df) 
W <- data.table(weights) 


DT[, `:=`(names(W), Map('*', DT[,names(W), with = F], W)), with = F] 
Questions connexes