web-dev-qa-db-fra.com

Appliquer une fonction à chaque colonne d'un bloc de données en observant le type de données existant de chaque colonne

J'essaie d'obtenir le min/max pour chaque colonne d'un grand bloc de données, dans le cadre de pour connaître mes données . Mon premier essai a été:

apply(t,2,max,na.rm=1)

Il traite tout comme un vecteur de caractères, car les premières colonnes sont des types de caractères. Donc max de certaines des colonnes numériques sort comme " -99.5".

J'ai alors essayé ceci:

sapply(t,max,na.rm=1)

mais il se plaint de max non significatif pour les facteurs . (lapply est identique.) Ce qui me trouble, c'est que apply pensait max était parfaitement significatif pour les facteurs, par exemple. il a renvoyé "ZEBRA" pour la colonne 1.

BTW, j'ai jeté un coup d'oeil à tilisation de sapply sur le vecteur de POSIXct et l'une des réponses dit " Lorsque vous utilisez sapply, vos objets sont forcés à être numériques, .. . ". Est-ce ce qui m'arrive? Si tel est le cas, existe-t-il une fonction d'application alternative qui ne contraint pas? C’est sûrement un besoin commun, car l’une des caractéristiques clés du type de trame de données est que chaque colonne peut être d’un type différent.

49
Darren Cook

Si c'était un "facteur ordonné", les choses seraient différentes. Ce qui ne veut pas dire que j'aime les "facteurs ordonnés", mais que certaines relations sont définies pour des "facteurs ordonnés" qui ne sont pas définis pour des "facteurs". Les facteurs sont considérés comme des variables catégoriques ordinaires. Vous voyez l'ordre de tri naturel des facteurs, qui correspond à l'ordre lexical alphabétique de votre région. Si vous voulez obtenir une contrainte automatique sur "numérique" pour chaque colonne, ... dates et facteurs et tout, essayez alors:

sapply(df, function(x) max(as.numeric(x)) )   # not generally a useful result

Ou si vous souhaitez d'abord tester les facteurs et revenir comme prévu, alors:

sapply( df, function(x) if("factor" %in% class(x) ) { 
            max(as.numeric(as.character(x)))
            } else { max(x) } )

Le commentaire de @Darrens fonctionne mieux:

 sapply(df, function(x) max(as.character(x)) )  

max réussit avec les vecteurs de caractères.

35
42-

La raison pour laquelle max fonctionne avec apply est que apply contraint d'abord votre trame de données à une matrice et qu'une matrice ne peut contenir qu'un seul type de données. Donc, vous vous retrouvez avec une matrice de caractères. sapply n'est qu'un wrapper pour lapply, il n'est donc pas surprenant que les deux produisent la même erreur.

Le comportement par défaut lorsque vous créez un cadre de données concerne les colonnes catégorielles à stocker sous la forme facteurs. Sauf si vous spécifiez qu'il s'agit d'un facteur ordonné, les opérations telles que max et min seront indéfinies, car R suppose que vous avez créé un non ordonné facteur.

Vous pouvez modifier ce comportement en spécifiant options(stringsAsFactors = FALSE), qui modifiera la valeur par défaut pour toute la session, ou vous pouvez passer stringsAsFactors = FALSE Dans l'appel de construction data.frame() lui-même. Notez que cela signifie simplement que min et max assumeront un ordre "alphabétique" par défaut.

Ou vous pouvez spécifier manuellement un ordre pour chaque facteur, bien que je doute que ce soit ce que vous souhaitiez faire.

Quoi qu'il en soit, sapply donnera généralement un vecteur atomique, ce qui impliquera de tout convertir en caractères dans de nombreux cas. Une solution est la suivante:

#Some test data
d <- data.frame(v1 = runif(10), v2 = letters[1:10], 
                v3 = rnorm(10), v4 = LETTERS[1:10],stringsAsFactors = TRUE)

d[4,] <- NA

#Similar function to DWin's answer          
fun <- function(x){
    if(is.numeric(x)){max(x,na.rm = 1)}
    else{max(as.character(x),na.rm=1)}
}   

#Use colwise from plyr package
colwise(fun)(d)
         v1 v2       v3 v4
1 0.8478983  j 1.999435  J
17
joran

Si vous voulez apprendre vos données, summary (df) fournit les colonnes min, 1er quantile, médian et moyen, 3e quantile et max des colonnes numériques ainsi que la fréquence des niveaux supérieurs des colonnes de facteurs.

7
Itamar

en s'appuyant sur la réponse de @ ltamar:
Utilisez le résumé et convertissez le résultat en quelque chose d’utile!

library(tidyr)
library(dplyr)

df %>% 
  summary %>% 
  data.frame %>%
  select(-Var1) %>%
  separate(data=.,col=Freq,into = c('metric','value'),sep = ':') %>%
  rename(column_name=Var2) %>%
  mutate(value=as.numeric(value),
         metric = trimws(metric,'both') 
  ) %>%  
  filter(!is.na(value)) -> metrics

Ce n'est pas joli et ce n'est certainement pas rapide mais ça fait le travail!

2
hibernado

Une solution utilisant retype() de hablar pour contraindre des facteurs à un caractère ou à un type numérique, en fonction de la faisabilité. J'utiliserais dplyr pour appliquer max à chaque colonne.

Code

library(dplyr)
library(hablar)

# Retype() simplifies each columns type, e.g. always removes factors
d <- d %>% retype()

# Check max for each column
d %>% summarise_all(max)

Résultat

Pas les nouveaux types de colonne.

     v1 v2       v3 v4   
  <dbl> <chr> <dbl> <chr>
1 0.974 j      1.09 J   

Données

# Sample data borrowed from @joran
d <- data.frame(v1 = runif(10), v2 = letters[1:10], 
                v3 = rnorm(10), v4 = LETTERS[1:10],stringsAsFactors = TRUE)
0
davsjob

Le meilleur moyen absolu de le faire est d’éviter les fonctions de base * apply, ce qui contraint l’ensemble du cadre de données à un tableau, et d’utiliser colwise de plyr . (Je suis surpris que personne n'en ait parlé)

Exemple utilisant parse_guess en tant que fonction opérant sur tous types de types de données vectoriels:

colwise(parse_guess)(t)

Réponse moins intéressante: on peut appliquer sur chaque colonne une boucle for:

for (i in 1:nrow(t)) { t[, i] <- parse_guess(t[, i]) }

Je ne connais pas de bonne façon de faire une assignation avec * appliquer tout en préservant la structure de trame de données .

0
qwr