web-dev-qa-db-fra.com

Sous-ensemble de données basé sur le nombre de lignes par groupe

J'ai des données comme celle-ci, où certains "nom" se produisent plus de trois fois:

df <- data.frame(name = c("a", "a", "a", "b", "b", "c", "c", "c", "c"), x = 1:9)

  name x
1    a 1
2    a 2
3    a 3
4    b 4
5    b 5
6    c 6
7    c 7
8    c 8
9    c 9

Je souhaite sous-ensemble (filtrer) les données en fonction du nombre de lignes (observations) à l'intérieur de chaque niveau de la variable name. Si un certain niveau de name se produit plus que disons 3 fois, je veux supprimer toutes les lignes appartenant à ce niveau. Donc, dans cet exemple, nous supprimerions des observations où name == c, car il y a > 3 lignes dans ce groupe:

  name x
1    a 1
2    a 2
3    a 3
4    b 4
5    b 5

J'ai écrit ce code, mais je ne peux pas le faire fonctionner.

as.data.frame(table(unique(df)$name))
subset(df, name > 3)
24
SJSU2013

D'abord, deux base alternatives. L'un s'appuie sur table, et l'autre sur ave et length. Ensuite, deux data.table façons.


1. table

tt <- table(df$name)

df2 <- subset(df, name %in% names(tt[tt < 3]))
# or
df2 <- df[df$name %in% names(tt[tt < 3]), ]

Si vous voulez le parcourir étape par étape:

# count each 'name', assign result to an object 'tt'
tt <- table(df$name)

# which 'name' in 'tt' occur more than three times?
# Result is a logical vector that can be used to subset the table 'tt'
tt < 3

# from the table, select 'name' that occur < 3 times
tt[tt < 3]

# ...their names
names(tt[tt < 3])

# rows of 'name' in the data frame that matches "the < 3 names"
# the result is a logical vector that can be used to subset the data frame 'df'
df$name %in% names(tt[tt < 3])

# subset data frame by a logical vector
# 'TRUE' rows are kept, 'FALSE' rows are removed.
# assign the result to a data frame with a new name
df2 <- subset(df, name %in% names(tt[tt < 3]))
# or
df2 <- df[df$name %in% names(tt[tt < 3]), ]

2. ave et length

Comme suggéré par @flodel:

df[ave(df$x, df$name, FUN = length) < 3, ]

3. data.table: .N et .SD:

library(data.table)
setDT(df)[, if (.N < 3) .SD, by = name]

4. data.table: .N et .I:

setDT(df)
df[df[, .I[.N < 3], name]$V1] 

Voir aussi les questions et réponses connexes Compter le nombre d'observations/lignes par groupe et ajouter le résultat au bloc de données .

53
Henrik

Utilisation du package dplyr:

df %>%
  group_by(name) %>%
  filter(n() < 4)

# A tibble: 5 x 2
# Groups:   name [2]
  name      x
  <fct> <int>
1 a         1
2 a         2
3 a         3
4 b         4
5 b         5

n() renvoie le nombre d'observations dans le groupe actuel, afin que nous puissions group_by nommer, puis conserver uniquement les lignes qui font partie d'un groupe où le nombre de lignes dans ce groupe est inférieur de 4.

32
Joe

Encore une autre façon d'utiliser le package dpylr consiste à utiliser la fonction count puis à effectuer une semi-jointure sur le bloc de données d'origine:

library(dplyr)

df %>% 
  count(name) %>%
  filter(n <= 3) %>%
  semi_join(df, ., by = "name")
1
Cettt