J'ai un ensemble de données contenant les informations suivantes:
total = 0; average = 0; for(i in 1:length(Data$Subject)){ for(j in 1:ncols(Data)){ if(Data$UniqueNumber[i] > 0){ total[i] = sum(Data[i,1:j]) average[i] = mean(Data[i,1:j]) } }
Si la valeur de UniqueNumber> 0, j'aimerais additionner les valeurs avec dplyr pour chaque sujet des lignes 1 à UniqueNumber et calculez la moyenne. Donc, pour le sujet 001, somme = 2 et moyenne = 0,67.
Subject Value1 Value2 Value3 UniqueNumber 001 1 0 1 3 002 0 1 1 2 003 1 1 1 1
Modifier: Je cherche uniquement à additionner le nombre de colonnes répertoriées dans la colonne «Numéro unique». Donc, cela parcourt chaque ligne et s'arrête à la colonne répertoriée dans 'UniqueNumber'. Exemple: la ligne 2 avec le sujet 002 doit additionner les valeurs des colonnes «Valeur1» et «Valeur2», tandis que la ligne 3 avec le sujet 003 ne doit additionner que la valeur de la colonne «Valeur1».
6 Réponses :
Une solution qui utilise purrr :: map_df
(qui est du même auteur que dplyr
).
tt <- "Subject Value1 Value2 Value3 UniqueNumber 001 1 0 1 3 002 0 1 1 2 003 1 1 1 1" dat <- read.table(text=tt, header=T)
Une autre option ( format de sortie plus proche d'une solution dplyr
):
map_df(l_dat, function(x) { n_cols <- x$UniqueNumber id <- x$Subject x <- as.numeric(x[2:(n_cols+1)]) tibble(id=id, mean_values=sum(x, na.rm=T)/(length(x)-1)) # change here }) # # A tibble: 3 x 2 # id mean_values # <int> <dbl> # 1 1 1. # 2 2 1. # 3 3 Inf #beware of this case where you end up dividing by 0
Juste à titre d'exemple j'ai ajouté un sum () code > puis divisé par
length(x)-1
:
map_df(l_dat, function(x) { n_cols <- x$UniqueNumber id <- x$Subject x <- as.numeric(x[2:(n_cols+1)]) tibble(id=id, mean_values=mean(x, na.rm=T)) }) # # A tibble: 3 x 2 # id mean_values # <int> <dbl> # 1 1 0.667 # 2 2 0.5 # 3 3 1
Data:
library(dplyr) library(purrr) l_dat <- split(dat, dat$Subject) # first we need to split in a list map_df(l_dat, function(x) { n_cols <- x$UniqueNumber # finds the number of columns x <- as.numeric(x[2:(n_cols+1)]) # subsets x and converts to numeric mean(x, na.rm=T) # mean to be returned }) # output: # # A tibble: 1 x 3 # `1` `2` `3` # <dbl> <dbl> <dbl> # 1 0.667 0.5 1
p >
Recevez l'erreur suivante lorsque j'exécute votre code: Erreur dans 2: (n_cols + 1): argument NA / NaN
Je n'ai pas cette erreur, l'avez-vous essayé sur mes données d'exemple? Si votre colonne "UniqueNumber" est nommée différemment, vous devez modifier cette partie x $ UniqueNumber
en conséquence.
Merci. J'ai mes données avaient une colonne manquante donc le code s'est écrasé. Je suis revenu et a corrigé le problème, cela fonctionne!
Pouvez-vous modifier le dénominateur de la fonction «moyenne» pour qu'elle divise par 1 de moins? Je dois inclure la première valeur (c'est-à-dire Value1), mais c'est un point de départ. Je voudrais donc diviser par un de moins dans chaque cas (tout en supprimant les NA).
@statsguyz oui vous pouvez, vous pouvez faire ce que vous voulez dans la fonction, changez simplement mean () avec ce que vous voulez, je vais mettre à jour avec un exemple.
Ce n'est pas un fan / expert tidyverse, mais j'essaierais cela en utilisant un format long. Ensuite, filtrez simplement par index de ligne par groupe, puis exécutez toutes les fonctions que vous souhaitez sur une seule colonne (beaucoup plus simple de cette façon).
library(data.table) data.table(Data) %>% melt(id = c("Subject", "UniqueNumber")) %>% .[as.numeric(gsub("Value", "", variable, fixed = TRUE)) <= UniqueNumber, .(Mean = round(mean(value), 3), Total = sum(value)), by = Subject] # Subject Mean Total # 1: 1 0.667 2 # 2: 2 0.500 1 # 3: 3 1.000 1
Une manière très similaire d'y parvenir pourrait être filtrage par les nombres entiers dans les noms de colonnes. L'étape de filtrage vient avant le group_by
donc elle pourrait potentiellement augmenter les performances (ou pas?) Mais elle est moins robuste car je suppose que les cols d'intérêt sont appelés "Value #"
Data %>% gather(variable, value, -Subject, -UniqueNumber) %>% #long format filter(as.numeric(gsub("Value", "", variable, fixed = TRUE)) <= UniqueNumber) %>% #filter group_by(Subject) %>% # group by Subject summarise(Mean = mean(value), Total = sum(value)) %>% # do the calculations ungroup() ## A tibble: 3 x 3 # Subject Mean Total # <int> <dbl> <int> # 1 1 0.667 2 # 2 2 0.5 1 # 3 3 1 1
Juste pour le plaisir, ajouter une solution data.table
library(tidyr) library(dplyr) Data %>% gather(variable, value, -Subject, -UniqueNumber) %>% # long format group_by(Subject) %>% # group by Subject in order to get row counts filter(row_number() <= UniqueNumber) %>% # filter by row index summarise(Mean = mean(value), Total = sum(value)) %>% # do the calculations ungroup() ## A tibble: 3 x 3 # Subject Mean Total # <int> <dbl> <int> # 1 1 0.667 2 # 2 2 0.5 1 # 3 3 1 1
Edit: On dirait que peu de sujets n'avaient pas de UniqueValues. Besoin de vérifier cela. Tout fonctionne bien!
Existe-t-il un moyen de modifier cela pour traiter les valeurs manquantes? Aussi, est-il possible de calculer la moyenne avec un dénominateur prenant en compte les valeurs manquantes?
Qu'entendez-vous par valeurs manquantes? NA
dans la colonne Value
? Ajoutez simplement na.rm = TRUE
aux fonctions, par exemple récapituler (Moyenne = moyenne (valeur, na.rm = TRUE), Total = somme (valeur, na.rm = TRUE))
. Je ne suis pas sûr de comprendre votre deuxième question. Pouvez-vous s'il vous plaît montrer un exemple avec la sortie souhaitée?
Oh ok, c'est ce que je pensais. Et si je voulais modifier la colonne "Moyenne" pour modifier le dénominateur de la fonction moyenne par +1 ou -1, est-ce possible?
Je ne suis pas sûr de comprendre ce que vous voulez dire, mais vous pouvez faire summary (Total = sum (value, na.rm = TRUE), Mean = Total / n ())
Vérifiez cette solution:
Subject UniqueNumber Sum Mean Value1 Value2 Value3 <chr> <int> <dbl> <dbl> <dbl> <dbl> <dbl> 1 001 3 2 0.667 1 0 1 2 002 2 1 0.5 0 1 1 3 003 1 1 1 1 1 1
Résultat:
df %>% gather(key, val, Value1:Value3) %>% group_by(Subject) %>% mutate( Sum = sum(val[c(1:(UniqueNumber[1]))]), Mean = mean(val[c(1:(UniqueNumber[1]))]), ) %>% spread(key, val)
Comment cela donne-t-il exactement les bons résultats? Cela me donne des résultats erronés lorsque j'insère des NA aléatoires dans les données. Par exemple, essayez d'insérer NA
dans Value1
dans la première ligne.
OP pourrait être intéressé uniquement par la solution dplyr
mais à des fins de comparaison et pour les futurs lecteurs une option de base R utilisant mapply
cols <- grep("^Value", names(df)) cbind(df, t(mapply(function(x, y) { if (y > 0) { vals = as.numeric(df[x, cols[1:y]]) c(Sum = sum(vals, na.rm = TRUE), Mean = mean(vals, na.rm = TRUE)) } else c(0, 0) },1:nrow(df), df$UniqueNumber))) # Subject Value1 Value2 Value3 UniqueNumber Sum Mean #1 1 1 0 1 3 2 0.667 #2 2 0 1 1 2 1 0.500 #3 3 1 1 1 1 1 1.000
Ici, nous sous-ensembles chaque ligne en fonction de son UniqueNumber
respectif, puis calculons sa somme
et mean
si la valeur de UniqueNumber
est supérieur à 0 ou bien ne renvoie que 0.
Voici une autre méthode qui utilise Créé le 2019-02- 14 par le package reprex (v0.2.1) tidyr :: nest
pour collecter les colonnes Values
dans une liste afin que nous puissions parcourir la table avec map2 code>. Dans chaque ligne, nous sélectionnons les valeurs correctes dans la colonne de liste
Valeurs
et prenons respectivement la somme ou la moyenne. library(tidyverse)
tbl <- read_table2(
"Subject Value1 Value2 Value3 UniqueNumber
001 1 0 1 3
002 0 1 1 2
003 1 1 1 1"
)
tbl %>%
filter(UniqueNumber > 0) %>%
nest(starts_with("Value"), .key = "Values") %>%
mutate(
sum = map2_dbl(UniqueNumber, Values, ~ sum(.y[1:.x], na.rm = TRUE)),
mean = map2_dbl(UniqueNumber, Values, ~ mean(as.numeric(.y[1:.x], na.rm = TRUE))),
)
#> # A tibble: 3 x 5
#> Subject UniqueNumber Values sum mean
#> <chr> <dbl> <list> <dbl> <dbl>
#> 1 001 3 <tibble [1 Ã 3]> 2 0.667
#> 2 002 2 <tibble [1 Ã 3]> 1 0.5
#> 3 003 1 <tibble [1 Ã 3]> 1 1
Je pense que le moyen le plus simple est de définir sur NA
les zéros qui devraient vraiment être NA
, puis d'utiliser rowSums
et rowMeans
sur le sous-ensemble approprié de colonnes.
Data <- structure( list(Subject = 1:3, Value1 = c(1L, 0L, 1L), Value2 = c(0L, 1L, NA), Value3 = c(1L, NA, NA), UniqueNumber = c(3L, 2L, 1L)), .Names = c("Subject","Value1", "Value2", "Value3", "UniqueNumber"), row.names = c(NA, 3L), class = "data.frame")
ou transform (Data, sum = rowSums (Data [2: 4], na.rm = TRUE), mean = rowMeans (Data [2: 4], na.rm = TRUE))
pour rester dans la base R.
data
Data[2:4][(col(dat[2:4])>dat[[5]])] <- NA Data # Subject Value1 Value2 Value3 UniqueNumber # 1 1 1 0 1 3 # 2 2 0 1 NA 2 # 3 3 1 NA NA 1 library(dplyr) Data%>% mutate(sum = rowSums(.[2:4], na.rm = TRUE), mean = rowMeans(.[2:4], na.rm = TRUE)) # Subject Value1 Value2 Value3 UniqueNumber sum mean # 1 1 1 0 1 3 2 0.6666667 # 2 2 0 1 NA 2 1 0.5000000 # 3 3 1 NA NA 1 1 1.0000000
Vous pouvez essayer
df%>% mutate (sum = ifelse (UniqueNumber> 0, rowSums (. [ 2: (length (.) - 1)]), NA), mean = ifelse (UniqueNumber> 0, rowMeans (. [ 2: (longueur (.) - 1)]), NA))
.@tmfmnk Je ne pense pas que votre code itérera sur la longueur de UniqueNumber. Il semble que mes résultats totalisent toute la colonne et ne s'arrêtent pas à la valeur de la colonne UniqueValue.