En mi experiencia como docente impartiendo cursos de Modelado Dimensional y de Power BI en general, me he encontrado ya en varias ocasiones con la necesidad de generar cantidades relativamente grandes de datos de prueba, especialmente en los cursos de modelado, cuando a menudo los alumnos quieren o necesitan probar las materias con datos que estén al menos levemente relacionados con su área de negocio.

Esto, por supuesto, es bastante incómodo, porque se acaban acumulando horas y horas tratando de encontrar un dataset relacionado con este o este otro campo, dependiendo mucho de la calidad de los mismos y dedicando un enorme esfuerzo a modelar unos datos que vienen de cualquier sitio. En ocasiones se encuentra un dataset ideal que nos soluciona la papeleta relativamente rápido, pero aún así la búsqueda suele ser bastante intensa y no siempre productiva.

Con esto en mente, y con la amargura de quien tropieza varias veces con la misma piedra, decidí generalizar y buscar una solución a mi necesidad de generar datos relativamente rápido y además aplicar a esos datos lógicas que luego se vean reflejadas en los modelos e informes que construyamos.

Herramientas

La idea es generar datos en un formato flexible cuya estructura pueda ser modificada según nuestras necesidades, que la cantidad de datos que generemos no impacte en el rendimiento de nuestra solución (que nos dé igual generar uno que mil) y que podamos aumentar arbitrariamente la cantidad de información, de manera que generemos algunos elementos de prueba de manera sencilla y en cierto punto podamos ampliar si lo necesitamos.

El formato más adecuado para estos requisitos son archivos JSON. Nos permite tener un esquema completamente flexible y podemos generar datos de a poquito, generando más archivos si necesitamos más datos. Además, al ser un formato relativamente legible por humanos, podríamos incluso modificar algún campo en algún lugar a mano si eso nos conviene para el modelo final.

Para generar los archivos utilizaremos scripts de python. Python es un lenguaje de programación que nos permite un bocetado rápido e incrementar la complejidad de la solución de manera relativamnete sencilla. Además, al ser un estándar de facto del análisis de datos será sencillo encontrar referencia y ejemplos para ampliar nuestra solución.

Además, Python nos ofrece una librería que nos va a ser extremadamente útil: Faker

Según el autor, Faker es un paquete de pyhon que genera datos falsos. Además de para generar datos de prueba puede ser útil para anonimizar datos sustituyendo información sensible por información generada.

Nos proporciona una serie de métodos para generar información por tipo. Así, tenemos métodos para generar nombres, direcciones, teléfonos, ciudades… Además, tiene opciones de localización, por lo que podemos asegurarnos de que los datos que se generan están en el contexto de un idioma. Por ejemplo, para castellano de España tenemos la localización es_ES

¿Necesitas ampliar tus conocimientos para impulsar tu proyecto?

Tanto si eres usuario de negocio sin experiencia y quieres dar tus primeros pasos en el Business Intelligence como si eres experto y quieres profundizar en tus conocimientos, tenemos algún curso para ti. Comprueba nuestro catálogo de formación en Power BI aquí.

Voy a echar un vistazo

Generar un JSON con Python

Establecido nuestro marco, vamos a comenzar con las pruebas. Utilizando Visual Studio Code y configurándolo para poder ejecutar Python, generamos una carpeta vacía para el proyecto, y creamos un nuevo archivo .py:

import json
def main():
j = {
       ‘test’: ‘Esto es una prueba’
}
filename = ‘test.json’
with open(filename,’w’) as jsonfile:
       jsonfile.write(json.dumps(j))

main()

Importando el paquete json accedemos a la serialización y deserialización de archivos json, que es la base del proyecto.

Montamos una función main() a la que vamos a llamar en el cuerpo del programa, y en la misma generamos un json de prueba, aquí llamado j definiendo la pareja clave-valor ‘test’: ‘Esto es una prueba’. A continuación abrimos un archivo para escritura y le escribimos sin más el volcado del json.

El resultado es un archivo json tal y como esperábamos:

Python Power BI json

Esto representa la base sobre la que trabajaremos: la idea es ir completando el json j en un bucle y llamando a faker para que vaya rellenando campos.

Generando personas

Para generar personas, lo primero que vamos a hacer va a ser montar una clase Persona que tenga como atributos los campos que queremos rellenar. La clase dispondrá de un metodo get_json() que compondrá el json a partir de dicha estructura.

Paso 1: Aislar la lógica

Generando datos de prueba con Python para Power BI

El método constructur de Person tendrá un atributo para cada dato generado. Cualquier lógica de la generación de datos quedará aquí aislada. Por ejemplo, si queremos generar un número al azar, deberíamos generarlo aquí.

El método que construye el json, get_json(self) se encarga de asignar efectivamente los datos generados en el constructor a la estructura del json, y devuelve el volcado del json, para simplificar la parte de generación de archivos. Si aparece alguna lógica en la asignación de campos, debe incluirse aquí.

Por último, en la función main() tenemos la instanciación de la clase y la creación del archivo. La lógica de nombrado de archivos ocupa esta parte (aunque más adelante la separaremos en una unción aparte por claridad.

El código queda así:

import json
class Person:
      def __init__(self):
                  self.test = ‘Esto es una prueba’
      def get_json(self):
                  j = {
                        ‘test’: self.test
                  }
                  return json.dumps(j)
def main():
      jsoncontents = Person()
      filename = ‘testperson.json’
      with open(filename,’w’) as jsonfile:
               jsonfile.write(jsoncontents.get_json())
main()

Paso 2: Generar una Persona

Con la estructura que hemos creado, está claro qué necesitamos para generar una persona: primero asignarle campos en el constructor y luego usar esos campos en la composición de un json en el método get_json. Vamos a generar una persona de prueba.

Necesitaremos importar el módulo datetime para poder generar una fecha (puede generarse directamente con un string, pero nos estamos adelantando a cómo generará las fechas el módulo de faker)

Montamos los siguientes campos en la entidad Person:
• Nombre
• Apellido
• Username
• Email
• Telefono
• Ciudad
• Numcuenta
• Nacimiento
Y les asignamos valores de ejemplo (cuidado con el datetime!)

A continuación, asignamos esos valores al json en la función get_json(self).


El resultado es algo así:

import json
import datetime
class Person:
def __init__(self):
           self.nombre = ‘José’
           self.apellido = ‘Martínez’
           self.username = ‘Pepe’
           self.email = ‘pepe@abc.com’
           self.telefono = ‘555-5555-1234’
           self.ciudad = ‘Alicante’
           self.numcuenta = ‘66048764759382421948’
           self.nacimiento = datetime.datetime(1980, 9, 30, 7, 6, 5)
def get_json(self):
       p = {
              ‘nombre’: self.nombre,
              ‘apellido’: self.apellido,
              ‘username’: self.username,
              ’email’: self.email,
              ‘telefono’: self.telefono,
              ‘ciudad’: self.ciudad,
              ‘numcuenta’: self.numcuenta,
              ‘nacimiento’: self.nacimiento.strftime(‘%m/%d/%Y’)
}
      return json.dumps(p)

def main():
       jsoncontents = Person()
       filename = ‘testperson.json’
      with open(filename,’w’) as jsonfile:
              jsonfile.write(jsoncontents.get_json())
main()

Al ejecutar, hay que tener en cuenta que vamos a sobreescribir el archivo del ejemplo anterior ya que no hemos cambiado la ruta del archivo:

Python Power BI json test person

Paso 3: Usar Datos Aleatorios

En nuestro archivo, ahora tendríamos que utilizar la librería faker en vez de cadenas escritas a mano al generar los datos en el constructor.

En primer lugar, importar la librería

from faker import Faker

Instanciar el generador de faker en la clase, y utilizarlo para generar datos:

def __init__(self):

        fake = Faker(‘es_ES’)

        self.nombre = fake.first_name()

        self.apellido = fake.last_name()

        self.username = self.nombre[:3] + self.apellido[:3]

        self.email = fake.email()

        self.telefono = fake.phone_number()

        self.ciudad = fake.city()

        self.numcuenta = fake.bban()

        self.nacimiento = fake.date_of_birth(minimum_age=18,

        maximum_age=100)

Notas sobre los datos generados:

  • El nombre de usuario toma los tres primeros caracteres del nombre y apellido ya generado
  • Note el constructor de la fecha de nacimiento: se le indica edad mínima y máxima y genera una fecha a partir de esos parámetros. De aquí que quisiésemos generar antes la fecha como un datetime y no como un string.

Como el generador del json no hace más que asignar a la estructura de json los campos ya calculados en el constructor, y como ya hemos previsto la conversión del datetime, no hace falta modificar nada más. Con esto ya tenemos un generador de personas aleatorias. Como aún no hemos tocado nada de la lógica de generación de archivos, volver a ejecutar el script sobrescribirá el mismo JSON, pero cada ejecución tendrá datos distintos, vean:

Generando datos de prueba con Python para Power BI
Python Power BI json test person 3
Python Power BI json test person 4

Paso 4: Generar Muchas Personas

Para generar muchas personas tenemos dos caminos, el sencillo y el menos sencillo. Como sencillo, tendríamos la opción de hacer un loop de llamadas al constructor y al generador de archivos. La menos sencilla sería generar una estructura de personas, una lista en concreto, en una entidad mayor que sería Gente (o People), y luego escribir esta lista en un solo json.

Para dejar un mecanismo terminado y funcionando, antes de entrar en más materia todavía, vamos a implementar ahora la versión de múltiples archivos. Para ello, vamos primero a aislar de la función main() la lógica que hay.

Montamos una función generatePerson() con la lógica que había en main(), y la referenciamos en el propio main():

def generatePerson():
       jsoncontents = Person()
       filename = ‘testperson.json’
       with open(filename,’w’) as jsonfile:
       jsonfile.write(jsoncontents.get_json())
def main():
      generatePerson()

Ahora, hacemos que generatePerson reciba un entero e itere por él para generar varias veces la entidad Person y generar así, distintos registros. Nótese que debemos incluír de alguna manera la variable por la que se itera en el nombre del archivo, para que no se sobreescriban:

def generatePerson(num):
       for n in range(0, num):
    jsoncontents = Person()
    filename = ‘testperson’ + str(n) +’.json’
    with open(filename,’w’) as jsonfile:
            jsonfile.write(jsoncontents.get_json())
def main():
     generatePerson(2)

Y al ejecutar, se han generado 2 json distintos:

Python Power BI json test person 5

Por último, en vez de introducir el número en la llamada sin más, utilizamos una variabel (que más adelante podremos parametrizar):

def main():

       numPersonas = 20

       generatePerson(numPersonas)

Python Power BI json test person 6

Donde todas estas personas son distintas, aleatorias, ¡y están en castellano!

Python Power BI json test person 7

Paso 5: Un solo JSON

Para terminar con el proceso de generaci’on de personas, vamos a realizar la generación del array People para tener todas las personas en el mismo archivo. Además de lo evidente (mejor un archivo grande que mil, a la hora de manejarse), la carga de este archivo con PowerBI es trivial, mientras que la carga de múltiples archivos json pequeños implica utilizar el conector de carpeta con el conector de JSON y puede complicarse un poquito el proceso 

La idea, como decíamos, es generar un array y para ello tenemos que realizar una modificación en la función generatePerson para que, primero monte un array con los elementos y luego se serialice ese array. Pero primero vamos a modificar el método get_json de la entidad Person para que no serialice el objeto. Queremos un array de json sin serializar, para luego, al serializarlos todos a la vez, el serializador monte un array. Así:

Renombramos el método get_json por get_item, por claridad, y quitamos de “return json.dumps(p)” la llamada al serializador, quedando de la siguiente manera:

def get_item(self,i):

        p = {

            ‘id_persona’: i,

            ‘nombre’: self.nombre,

            ‘apellido’: self.apellido,

            ‘username’: self.username,

            ’email’: self.email,

            ‘telefono’: self.telefono,

            ‘ciudad’: self.ciudad,

            ‘numcuenta’: self.numcuenta,

            ‘nacimiento’: self.nacimiento.strftime(‘%m/%d/%Y’)

        }

        return (p)

Luego, de vuelta en generatePerson, lo modificamos para que primero se monte la lista de personas (en json sin serializar, es decir, Person().get_item() ) y luego se serialice todo a la vez.

 

def generatePerson(num):

    People = [Person().get_item(i) for i in range(num)]

    filename = ‘people.json’

    with open(filename,’w’) as jsonfile:

        jsonfile.write(json.dumps(People))

Así, generamos un gran archivo que, al cargarlo en PowerBI sin hacer nada más (obtener datos >> Json >> Seleccionar el archivo) tiene la siguiente apariencia:

Python Power BI json test person table

Generando Hechos

Ahora que podemos crear tantas entidades Persona como queramos y guardarlas en un array, vamos a montar un hecho “venta” para poder añadir a nuestro informe.

En primera instancia, podríamos generar un array de dicho objeto “venta” dentro de la persona aprovechando así la débil estructuración de los datos en JSON. Otra alternativa sería generar un array distinto para las ventas, pero vamos a mantenernos generando un único json para todo el historial.

Así, lo primero que necesitamos es una entidad de venta, que llamaremos Operation y almacenaremos en un campo “historial” para guardar el historial de ventas dentro de persona.

La entidad Operation bien podría ser así, por ahora:

import random

class Operation:

    def __init__(self):

        fake = Faker(‘es_ES’)

        self.valor = round(random.random()*random.random()*10000)

        self.tienda = fake.company()

    def get_item(self):

        p = {

            ‘valor’: self.valor,

            ‘tienda’: self.tienda 

        }

        return (p)

Estamos generando un campo “valor” que será el valor de la operación; y un campo tienda utilizando nuestra librería Faker, que genera nombres de compañías inventados. Nótese que para el valor estamos multiplicando dos números aleatorios entre sí y luego multiplicando por 10000. Random devuelve un número entre cero y uno y al multiplicar dos de estos numeros generados al azar lo que hacemos es bajar la probabilidad de que el número final se acerque al máximo (10000 en este caso) y habrá más números pequeños que grandes.

Además, necesitamos modificar la persona, de la siguiente manera:

class Person:

    def __init__(self):

        fake = Faker(‘es_ES’)

        self.nombre = fake.first_name()

       […]

        self.historial = [Operation().get_item()]

    def get_item(self):

        p = {

            ‘nombre’: self.nombre,

            […]

            ‘historial de ventas’: self.historial

        }

        return (p)

Ojo al hecho de que estamos metiendo una operación sin más dentro de una lista en el campo Historial. No es una operación suelta, sino es una lista que contiene una operación.

Generando el archivo json y volviendo a Power BI vemos que las personas ahora tienen un campo llamado “historial de ventas” que tiene dentro una lista (las operaciones, aunque sólo hay una)

Python Power BI json historial ventas

Y al expandir esa lista vemos las operaciones de las personas:

Generando datos de prueba con Python para Power BI

Conclusiones

Este es sólo el principio! Podemos extender la lógica de la generación de las ventas, por ejemplo, para simular el paso del tiempo; y podemos hacer depender unas características generadas de otras. ¿No tendría más lógica que las ventas fuesen más comunes en la ciudad de origen? ¿Podríamos tener un saldo para cada cliente que se fuese consumiendo con las ventas?

Los siguientes pasos irían por ahí: separar las entidades y hacerlas depender unas de otras.

Python Power BI json report

¡Has llegado al final! Parece que te ha gustado nuestro post sobre BI

Recuerda que, tanto si eres usuario de negocio sin experiencia y quieres dar tus primeros pasos en BI como si eres experto y quieres profundizar en tus conocimientos, tenemos algún curso para ti. Comprueba nuestro catálogo de formación en Power BI aquí.

Voy a Echar un vistazo
0 Shares:
Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

You May Also Like
Arquitectura Power BI Premium
Leer más

Arquitecturas Power BI Premium

Repasamos las posibles arquitecturas que puede tomar un proyecto de BI, partiendo de un escenario de Power BI Premium y en el que principalmente el dato viene de orígenes ya estructurados, analizando las principales ventajas e inconvenientes de cada uno, para poder elegir siempre el mejor camino.
Gestión desatendida de tablas estructuralmente cambiantes
Leer más

Gestión desatendida de tablas estructuralmente cambiantes

Revisaremos como podemos afrontar la carga de tablas que pueden cambiar a nivel estructural, de forma que no sea necesaria ninguna modificación en el proceso de carga si tiene lugar algún borrado o insercion de columnas nuevas; esto es, tendremos un proceso de carga totalmente desatendido.