2010-12-06 46 views
19

OK, nous sommes tous familiers avec double opérateur deux points dans l'affaire R. Chaque fois que je suis sur le point d'écrire une fonction, j'utilise require(<pkgname>), mais je pense toujours à l'aide :: à la place. L'utilisation de require dans les fonctions personnalisées est une meilleure pratique que library, puisque require renvoie l'avertissement et FALSE, contrairement à library, qui renvoie une erreur si vous indiquez un nom de package inexistant. D'autre part, l'opérateur :: obtient la variable de l'emballage, alors que require charge l'ensemble du paquet (du moins je l'espère), donc les différences de vitesse sont apparues en premier. :: doit être plus rapide que require.R style d'écriture - exigent par rapport ::

Et je l'ai fait une analyse afin de vérifier que - j'ai écrit deux fonctions simples qui charge read.systat fonction du paquet foreign, avec require et :: respectivement, par conséquent importer Iris.syd ensemble de données livré avec paquet foreign, fonctions répliquées 1000 fois chacun (qui était sans vergogne arbitraire), et ... croqué quelques chiffres. Bizarrement (ou pas) j'ai trouvé des différences significatives en termes de CPU utilisateur et de temps écoulé, alors qu'il n'y avait pas de différences significatives en termes de CPU système. Et encore plus étrange conclusion: :: est en réalité plus lent! Documentation pour :: est très contondant, et juste en regardant les sources, il est évident que :: devrait fonctionner mieux!

nécessitent

#!/usr/local/bin/r 

## with require 
fn1 <- function() { 
    require(foreign) 
    read.systat("Iris.syd", to.data.frame=TRUE) 
} 

## times 
n <- 1e3 

sink("require.txt") 
print(t(replicate(n, system.time(fn1())))) 
sink() 

côlon à double

#!/usr/local/bin/r 

## with :: 
fn2 <- function() { 
    foreign::read.systat("Iris.syd", to.data.frame=TRUE) 
} 

## times 
n <- 1e3 


sink("double_colon.txt") 
print(t(replicate(n, system.time(fn2())))) 
sink() 

Grab données CSV here. Quelques statistiques:

user CPU:  W = 475366 p-value = 0.04738 MRr = 975.866 MRc = 1025.134 
system CPU: W = 503312.5 p-value = 0.7305 MRr = 1003.8125 MRc = 997.1875 
elapsed time: W = 403299.5 p-value < 2.2e-16 MRr = 903.7995 MRc = 1097.2005 

MRR est de rang moyen pour require, ibid MRc pour ::. Je dois avoir fait quelque chose de mal ici. Cela n'a tout simplement aucun sens ... Le temps d'exécution pour :: semble beaucoup plus rapide !!! J'ai peut-être foiré quelque chose, vous ne devriez pas abandonner cette option ...

OK ... J'ai perdu mon temps afin de voir qu'il y a une différence, et j'ai effectué une analyse complètement inutile, donc , revenir à la question:

"Pourquoi doit-on préférer require sur :: lors de l'écriture d'une fonction?"

=)

+0

Est-ce pour les fonctions autonomes ou fonctions dans un package? – hadley

+0

En outre, vous auriez normalement besoin de() une fois en haut de votre script, pas une fois dans chaque appel de fonction. – hadley

+1

C'est pour les fonctions autonomes. Je développe une application web, et comme RApache démarre une nouvelle session R à chaque requête HTTP, j'essaie d'éviter une charge de serveur inutile. Cet exemple est inapproprié - une fois que vous avez importé un fichier, le travail est terminé, mais dans une application Web interactive avec un tas d'appels AJAX, cela peut être très inefficace. – aL3xa

Répondre

12

« Pourquoi faut-il préférer exiger plus :: lors de l'écriture d'une fonction? "

Je préfère habituellement require en raison de la belle valeur de retour VRAI/FAUX qui me permet de faire face à la possibilité de l'emballage ne soient pas disponibles à l'avant avant d'entrer dans le code. Crash le plus tôt possible au lieu de la moitié de votre analyse.

Je n'utilise que :: quand je dois m'assurer que j'utilise la bonne version d'une fonction, pas une version d'un autre paquet qui masque le nom.

D'autre part, l'opérateur obtient :: la variable de l'emballage, tout en exigent des charges paquet entier (au moins je l'espère), de sorte que les différences de vitesse sont venus premier à l'esprit. :: doit être plus rapide qu'exiger.

Je pense que vous pouvez ignorer les effets de lazy loading qui est utilisé par le paquet foreign selon the first page of its manual. Essentiellement, les paquets qui utilisent un chargement différé retardent le chargement des objets, tels que les fonctions, jusqu'à ce que les objets soient appelés pour la première fois. Donc, votre argument selon lequel ":: doit être plus rapide que require" n'est pas forcément vrai car foreign ne charge pas tout son contenu en mémoire lorsque vous l'attachez avec require. Pour plus de détails sur le chargement paresseux, voir Prof. Ripley's article dans RNews, volume 4, numéro 2.

+0

Vous avez tellement raison ... et cela peut être un problème dépendant du paquet. Oh, et merci pour la référence. – aL3xa

6

Depuis le temps de charger un paquet est presque toujours petit par rapport au temps que vous passez à essayer de comprendre ce que le code que vous avez écrit six mois Il y a eu, dans ce cas, le codage de la clarté est la chose la plus importante.

Pour les scripts, avoir un appel à require ou library au début vous permet de savoir quels paquets dont vous avez besoin immédiatement.

De même, appeler require (ou un emballage comme requirePackage dans Hmisc ou try_require en ggplot2) au début d'une fonction est la façon la plus univoque de montrer que vous devez utiliser ce package.

:: devrait être réservée aux cas où vous avez les conflits de noms entre les paquets, par exemple – comparez,

Hmisc::is.discrete 

et

plyr::is.discrete