Datanalytics

Archivo

Entradas Etiquetadas ‘ley de benford’

La ley de Benford, revisitada

Martes, 20 de septiembre de 2011 1 comentario

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.

Categories: estadística, r Tags: , ,

La ley de Benford

Jueves, 15 de septiembre de 2011 5 comentarios

El otro día me preguntó una compañera el motivo por el que un proceso (de transformación de datos) se ejecutaba tan lentamente. De oficio, siempre hago lo mismo —además, lo saben: ¿para qué seguirán preguntando?—: ejecutar el proceso sólo sobre un porcentaje de los datos.

Con los que el id acababa en 123, era inmediato; con 12, también; con 1, se eternizaba. Pero con 2, 3 y 4 volvía a ser muy rápido. ¡Había muchísimos registros con id acabado en 1!

Me llamó la atención y se lo dije: entendería, por la ley de Benford, que ocurriese eso con los id que comienzan por 1, ¡pero no con los que terminan con dicha cifra! Pero por la cara que puso, sospeché que no estaba al tanto de las leyes del buen señor. Así que la remití a la Wikipedia y volví a mis asuntos.

Y creo que le hice un disfavor. La página sobre la ley de Benford —y, especialmente, la sección sobre su explicación— es atrozmente confusa cuando no falaz. En particular, da una relevancia especial a la distribución exponencial, cuando ésta tiene, a lo más, una relación histórica con el fenómeno (dado que fue advertido por primera vez al examinar tablas de logaritmos).

La ley de Benford tiene que ver con la función de distribución de los valores de una lista de números. Generalmente, en la práctica, estos siguen distribuciones de cola larga: los ingresos de las personas, el tamaño de los municipios, la capitalización bursátil de las empresas, etc., tienen distribuciones similares a las de Weibull, exponencial, Pareto, etc.

Siempre que la distribución tenga una forma similar a la de la gráfica (extraída de la Wikipedia) que aparece encima, el tramo [10, 20) será más probable que el [20,30) y este que el [30, 40). Lo mismo pasa con los tramos [100, 200) y [200,300). Etc. En resumen, hay ley de Benford porque la función de densidad es decreciente.

Además, las probabilidades de ocurrencia de cada cifra no son taumatúrgicamente las que nos quieren hacer creer los wikipedistas. Son las que son en cada caso como demuestra el siguiente bloque de código:

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 )

Nota: (para los amigos del y esto para qué) dizque la ley de Benford se aplica para detectar patrones de fraude.

Categories: estadística, r Tags: , ,