2016-02-04 15 views
7

Spesso mi trovo a provare a creare una variabile categoriale da una variabile numerica + un insieme di intervalli fornito dall'utente.R: creazione di una variabile categoriale da una variabile numerica e intervalli personalizzati/aperti/a valore singolo

Ad esempio, dire che ho un data.frame con una variabile numerica df$V e vorrebbe creare una nuova variabile df$VCAT tale che:

  • df$VCAT = 0 se df$V è uguale a 0
  • df$VCAT = 1 se df$V è compreso tra 0 e 10 (cioè (0,10))
  • df$VCAT = 2 è df$V è pari a 10 (cioè [10,10])
  • df$VCAT = 3 è df$V è compreso tra 10 e 20 (ad es. (10,20))
  • df$VCAT = 4 è df$V è maggiore o uguale a oltre il 20 (cioè [20, Inf])

Attualmente sto facendo questo difficile codifica la "funzione di scoring" me stesso fare qualcosa di simile:

df = data.frame(V = seq(1,100)) 
df = df %>% mutate(VCAT = (V>0) + (V==10) + 2*(V>10) (V>=20))` 

mi chiedo se c'è un modo hacky più facile farlo in R, preferibilmente utilizzando dplyr (in modo che possa comandi a catena). Idealmente, sto cercando una funzione breve che possa essere utilizzata in mutate che includa la variabile V e un vettore che descrive gli intervalli come buckets. Nota che buckets potrebbe non essere descritto nel modo migliore in quanto non mi è chiaro come consentirebbe agli utenti di personalizzare gli endpoint degli intervalli.

+2

Sai di 'cut()'? Controlla '? Cut' o forse anche' Hmisc :: cut2() '. – JasonAizkalns

+0

Vuoi che la tua funzione acquisisca un vettore più 'bucket' e restituisca un frame di dati che assomiglia al risultato di quanto sopra? O vuoi una funzione che prende un vettore e 'bucket' che possono essere passati a 'mutate'? – jamieRowen

+2

@jamieRowen preferibilmente qualcosa che potrebbe essere passato a 'mutare'. –

risposta

2

Un modo in cui i numeri bin sono di rimuovere il resto utilizzando l'opuscolo modulo, %%. Per esempio. a bin in gruppi di 20:

#create raw data 
unbinned<-c(1.1,1.53,5,8.3,33.5,49.22,55,57.9,79.6,81,95,201,213) 
rawdata<-as.data.frame(unbinned) 

#bin the data into groups of 20 
binneddata<-mutate(rawdata,binned=unbinned-unbinned %% 20) 

#print the data 
binneddata 

Questo produce l'uscita:

unbinned binned 
1  1.10  0 
2  1.53  0 
3  5.00  0 
4  8.30  0 
5  33.50  20 
6  49.22  40 
7  55.00  40 
8  57.90  40 
9  79.60  60 
10 81.00  80 
11 95.00  80 
12 201.00 200 
13 213.00 200 

Così 0 rappresenta 0- < 20, 20 rappresenta 20- < 40, 40, 40- 60 < ecc (naturalmente dividere il valore binned da 20 per ottenere gruppi sequenziali come nella domanda originale)

bonus

Se si desidera utilizzare i valori aggiunti come variabili categoriali in ggplot ecc. Convertendoli in stringhe, ordineranno in modo strano, ad es. 200 arriverà prima del 40, perché '2' viene prima di '4' nell'alfabeto, per aggirare questo, utilizzare la funzione sprintf per creare zeri iniziali.(Il 3 in %03d dovrebbe essere il numero di cifre che ci si aspetta il numero più lungo per essere):

#convert the data into strings with leading zeros 
binnedstring<-mutate(binneddata,bin_as_character=sprintf('%03d',binned)) 

#print the data 
binnedstring 

dando l'output:

unbinned binned bin_as_character 
1  1.10  0    000 
2  1.53  0    000 
3  5.00  0    000 
4  8.30  0    000 
5  33.50  20    020 
etc. 

Se si desidera avere 000-<020, creare il limite superiore utilizzando aritmetica e concatenare utilizzando la funzione incolla:

#make human readable bin value 
binnedstringband<-mutate(
    binnedstring, 
    nextband=binned+20, 
    human_readable=paste(bin_as_character,'-<',sprintf('%03d',nextband),sep='') 
) 

#print the data 
binnedstringband 

Dare:

unbinned binned bin_as_character nextband  human_readable 
1  1.10  0    000  20   000-<020 
2  1.53  0    000  20   000-<020 
3  5.00  0    000  20   000-<020 
4  8.30  0    000  20   000-<020 
5  33.50  20    020  40   020-<040 
etc.