La ley de Benford, revisitada

Revisito mi artículo sobre la ley de Benford no tanto por hacer mención a las entradas una, dos y tres que hizo Gregorio Serrano en su bitácora ni al oportunísimo artículo de The Guardian al respecto. Ni siquiera para mencionar la existencia de este sesudo artículo sobre el tema.

Lo hago porque me pliego a la demanda popular: voy a explicar con más detalle el código que dejé allí escrito y que, por referencia, es

benford <- function( foo, ..., n = 100000 ){
  tmp <- foo( n, ... )
  tmp <- as.character( tmp[ tmp > 0] )
  tmp <- strsplit( tmp, "" )

  leading.digit <- function( x )
    x[ ! x %in% c( "0", "." )][1]

  tmp <- unlist( lapply( tmp, leading.digit ) )
  100 * table( tmp ) / length( tmp )
}

benford( rcauchy )
benford( rexp, rate = 2 )
benford( rexp, rate = 5 )
benford( rnorm, sd = 40 )
benford( rweibull, shape = 1 )

Puede llamar la atención que el primer argumento de la función benford sea, precisamente, otra función. Nada del otro mundo. El siguiente es un ejemplo en el que se muestra el uso aislado para una mejor comprensión:

funcion.que.llama <- function( foo, ... ){
  foo(...)
}

funcion.que.llama( sin, 2*pi )
funcion.que.llama( plot, iris, main = "Hola" )

La función funcion.que.llama recibe una función como argumento. También recibe argumentos adicionales arbitrarios (eso es lo que significa ...). Dentro, además de llamar a la función elegida, le pasa los argumentos adicionales contenidos en .... Es su uso más habitual, según el documento que define R.

Después uso las funciones as.character (para convertir el número en una cadena de texto) y strsplit, función análoga a la que existe en otros lenguajes. Su particularidad en R es que, aplicada a un vector de caracteres, devuelve una lista de la misma longitud que el vector original. No hay más que saber sobre dicha función que entender cuál es la salida de

strsplit( c("Hola", "caracola"), "l")

Como strsplit devuelve una lista, resulta conveniente utilizar lapply para recorrerla y extraer de cada componente el primer caracter. La lógica de lo que se quiere hacer en cada componente está encapsulada en la función de usar y tirar leading.digit.

Finalmente, como lapply devuelve una lista, utilizo unlist para convertirla en una estructura más simple, un vector de characters sobre el que utilizo la función table para calcular las frecuencias.

En resumen, llamo a las funciones as.character, strsplit y table porque sirven para mis fines: aportan el contenido semántico. Las llamadas a lapply y unlist no tienen otro objeto que facilitar la manipulación de los objetos que devuelven las anteriores. Son, si se quiere decir así, pegamento sintáctico.