2017-10-03 7 views
2

J'essaie de comprendre comment manipuler les données here. L'image ne montre qu'un seul cours, mais j'ai plusieurs cours et numéros de cours qui vont de 2010 à 2017. Comment dois-je ajouter une colonne indiquant la note médiane pour un certain cours basé sur l'année, enseigné et à terme? Nous avons le nombre d'enfants qui ont obtenu une certaine note, mais pas les notes réelles. Je m'attends à ce que la colonne de la note médiane devrait 11 dupliquer pour les 11 différents grades en fonction de chaque variable «enseignée». Enseigné ne peut avoir que deux valeurs, soit "ici" ou "là".Obtenir la médiane implicitement dans R de 2 colonnes

J'ai essayé d'utiliser la fonction d'agrégat mais ce problème ne semble pas pouvoir être résolu avec des fonctions de haut niveau. Le DB est DBKids dans R. Je ne peux pas sembler penser à un moyen qui peut m'aider avec ce problème. Merci!

Edit: Code Reproductibles

structure(list(sessionYear = c(2010, 2010, 2010, 2010, 2010, 
2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 
2010, 2010, 2010, 2010, 2010, 2010), courseNumber = c("20", "20", 
"20", "20", "20", "20", "20", "20", "20", "20", "20", "20", "20", 
"20", "20", "20", "20", "20", "20", "20", "20", "20"), 
courseName = c("KidsLearn", "KidsLearn", "KidsLearn", "KidsLearn", 
"KidsLearn", "KidsLearn", "KidsLearn", "KidsLearn", "KidsLearn", 
"KidsLearn", "KidsLearn", "KidsLearn", "KidsLearn", "KidsLearn", 
"KidsLearn", "KidsLearn", "KidsLearn", "KidsLearn", "KidsLearn", 
"KidsLearn", "KidsLearn", "KidsLearn"), Taught = c("There", 
"Here", "There", "Here", "There", "Here", "There", 
"Here", "There", "Here", "There", "Here", "There", 
"Here", "There", "Here", "There", "Here", "There", 
"Here", "There", "Here"), Term = c("1", "1", "1", "1", "1", 
"1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", 
"1", "1", "1", "1"), averageGrade = c(83, 84, 83, 84, 83, 84, 
83, 84, 83, 84, 83, 84, 83, 84, 83, 84, 83, 84, 83, 84, 83, 84 
), Grade = c("F", "F", "D", "D", "C3", "C3", "C2", "C2", "C1", 
"C1", "B3", "B3", "B2", "B2", "B1", "B1", "A3", "A3", "A2", "A2", 
"A1", "A1"), numberOfKids = c(1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 
3, 0, 3, 2, 6, 0, 14, 7, 24, 4, 18, 4)), class = "data.frame", row.names = c(NA, 
-22L), .Names = c("sessionYear", "courseNumber", "courseName", 
"Taught", "Term", "averageGrade", "Grade", "numberOfKids")) 

Hope this helps.

+0

Choisissez et suivez votre méthode de groupe/agrégat préférée à partir de la R-FAQ [Comment calculer moyenne par groupe?] (Https://stackoverflow.com/q/11562656/903061), mais remplacez «mean» par «weighted» .median' du paquet 'spatstat'. ([Ou l'un des autres paquets qui l'implémentent] (https://www.rdocumentation.org/search?q=weighted.median&latest=1)). Vous aurez probablement besoin de convertir les niveaux de facteur en nombre entier avec 'as.integer' (assurez-vous que les niveaux sont ordonnés correctement!) – Gregor

+1

Il est plus facile de vous aider si vous fournissez un [exemple reproductible] (https://stackoverflow.com/questions/5963269/comment-faire-un-grand-r-reproductible-exemple). Les images de données ne sont pas très utiles. Essayez un 'dput()' ou utilisez un exemple de jeu de données intégré pour illustrer votre problème. Donne la sortie désirée. – MrFlick

+0

J'ai ajouté le code. Merci pour la suggestion! –

Répondre

0

D'abord, nous allons faire un factor de grade, en s'assurant que les niveaux sont dans le bon ordre. Cela nous pouvons convertir en numérique, donc nous avons des chiffres pour prendre la médiane de.

levels(factor(dd$Grade)) 
# [1] "A1" "A2" "A3" "B1" "B2" "B3" "C1" "C2" "C3" "D" "F" 

## order seems good 

dd$grade_numeric = as.numeric(factor(dd$Grade)) 

Maintenant, nous faisons la médiane par groupe, pondéré par le nombre d'enfants, rond entier le plus proche et reconvertir au grade lettre.

library(dplyr) 
group_by(dd, sessionYear, Taught, Term) %>% 
    mutate(med = spatstat::weighted.median(x = grade_numeric, w = numberOfKids), 
      med = round(med), 
      median_Grade = levels(factor(Grade))[med]) %>% 
    print.data.frame 
# sessionYear courseNumber courseName Taught Term averageGrade Grade numberOfKids grade_numeric med median_Grade 
# 1   2010   20 KidsLearn There 1   83  F   1   11 2   A2 
# 2   2010   20 KidsLearn Here 1   84  F   0   11 2   A2 
# 3   2010   20 KidsLearn There 1   83  D   0   10 2   A2 
# 4   2010   20 KidsLearn Here 1   84  D   0   10 2   A2 
# 5   2010   20 KidsLearn There 1   83 C3   1    9 2   A2 
# 6   2010   20 KidsLearn Here 1   84 C3   0    9 2   A2 
# 7   2010   20 KidsLearn There 1   83 C2   1    8 2   A2 
# 8   2010   20 KidsLearn Here 1   84 C2   0    8 2   A2 
# 9   2010   20 KidsLearn There 1   83 C1   0    7 2   A2 
# 10  2010   20 KidsLearn Here 1   84 C1   0    7 2   A2 
# 11  2010   20 KidsLearn There 1   83 B3   3    6 2   A2 
# 12  2010   20 KidsLearn Here 1   84 B3   0    6 2   A2 
# 13  2010   20 KidsLearn There 1   83 B2   3    5 2   A2 
# 14  2010   20 KidsLearn Here 1   84 B2   2    5 2   A2 
# 15  2010   20 KidsLearn There 1   83 B1   6    4 2   A2 
# 16  2010   20 KidsLearn Here 1   84 B1   0    4 2   A2 
# 17  2010   20 KidsLearn There 1   83 A3   14    3 2   A2 
# 18  2010   20 KidsLearn Here 1   84 A3   7    3 2   A2 
# 19  2010   20 KidsLearn There 1   83 A2   24    2 2   A2 
# 20  2010   20 KidsLearn Here 1   84 A2   4    2 2   A2 
# 21  2010   20 KidsLearn There 1   83 A1   18    1 2   A2 
# 22  2010   20 KidsLearn Here 1   84 A1   4    1 2   A2 

Dans cet exemple, il n'y a que deux groupes (durée et année seulement ont une valeur chacun), et les deux ont une note moyenne de A2. (Faites défiler vers la droite pour voir les colonnes ajoutées.)

1

Donc, chaque entrée numberOfKids est le nombre d'enfants ayant obtenu la note correspondante en Grade? Vous pouvez obtenir la médiane « à la main » en faisant

get_median = function(numberOfKids,Grade){ 
current_count = 0 
middle = (sum(numberOfKids)+1)/2 
for (i in 1:length(numberOfKids)){ 
    current_count = current_count+numberOfKids 
    #if we're halfway through the class, return the current grade 
    if (current_count == middle) return(Grade[i]) 
    #if we're more than halfway through the class, then decide whether 
    #the middle is closer to the current total or the previous 
    if (current_count > middle){ 
    if ((current_count-middle) > numberOfKids[i]/2) return(Grade[i]) 
    return(Grade[i-1] } } } 

Normalement avec une médiane, s'il y a un « lien », vous prenez la moyenne des deux valeurs, mais vous ne pouvez pas vraiment prendre la moyenne de deux grades, de sorte que vous devez décider lequel choisir. Avec cette fonction, s'il y a une cravate complète, il faut le grade inférieur. Vous pouvez changer cela en changeant le dernier ">" en "> =".

+0

Il pourrait y avoir un moyen un peu plus simple avec 'cumsum' et' findInterval'. Bonne approche! – Gregor