En esta entrada de blog mostraremos como crear un calendario en Power Query para distintos casos de uso, donde las fechas inicial y final pueden ser valores fijos o depender de la fecha actual, o de una o varias columnas de fechas.
En Power BI Desktop podemos crear calendarios en Power Query o usando funciones DAX. Podemos escoger cualquiera de las dos opciones para hacerlo y el resultado es muy similar. Cuando creamos un calendario en Power Query, podemos conectarnos a páginas web que contengan los calendarios festivos de nuestro país o región e incorporarlos a nuestro calendario, esta es la principal ventaja de crear un calendario en Power Query y no con DAX.
Los pasos para crear un calendario en Power Query son siempre los mismos, independientemente del caso de uso:
- Especificar las fechas de inicio y fin del calendario
- Crear una lista con las fechas
- Convertir la lista en una tabla
- Adicionar a la tabla las columnas de año, número de mes y nombre de mes.
- Añadir las columnas trimestre, semana, día de la semana y AAMM
- Agregar columna con Fechas fiscales: mes, año y AAMM
- Añadir columna con festivos
Podemos añadir otras columnas dependiendo de nuestras necesidades.
Mostraremos 5 casos de uso para crear un calendario que solo difieren en como se tomen los valores de fecha inicial y final para crearlo, el resto de columnas que se creen serán las mismas en todos los casos.
Los valores de fecha que utilizaremos son:
- Dos valores de fechas fijos: fecha inicial y fecha final.
- Los 3 últimos años hasta la fecha actual, desde la misma fecha hace tres años hasta la fecha actual.
- Los 3 últimos años completos, desde el primer día del año de hace tres años hasta el último día del año actual.
- Los valores mínimo y máximo de una columna de fechas, donde el menor valor será la fecha inicial y el mayor valor la fecha final.
- Los valores mínimo y máximo de varias columnas de fechas. Seleccionaremos el menor valor entre todas las columnas como valor inicial y el mayor valor como valor final del calendario.
Desarrollaremos el primer caso de uso completo, es decir, crearemos un calendario con todas las columnas descritas. A continuación, convertiremos el calendario en una función con todas las transformaciones que hemos realizado en este caso de uso. Finalmente crearemos cinco consultas para crear los calendarios correspondientes a cada caso de uso y que utilizarán esta función.
Crear un calendario a partir de dos valores de fechas fijos, fecha inicial y fecha final.
Pasos:
- Declaramos dos parámetros, uno para la fecha inicial, que nombraremos FI y le asignamos el valor 01/01/2019,
- Crearemos una consulta en blanco y la nombramos fCalendario.
- Abrimos el Editor avanzado y sustituimos el paso Origen por la fórmula:
y otro para la fecha final, que nombraremos FF y al que le asignamos el valor 31/12/2020.
Si quieres conocer más acerca de los parámetros puedes visitar una entrada anterior donde hablamos de su uso y cómo crearlos.
Duracion = Duration.TotalDays (FF-FI) + 1La expresión calcula la diferencia en días entre las dos fechas.
- Añadimos un nuevo paso usando la transformación:
ListaFechas = List.Dates(FI,Duracion,#duration(1,0,0,0))Este paso genera una lista de fechas desde la fecha inicial hasta la fecha final.
- A continuación, transformamos la lista en una tabla mediante la fórmula:
TablaFechas = Table.FromList(ListaFechas, Splitter.SplitByNothing(), type table [Fecha = date], null, ExtraValues.Error)
La función Table.FromList convierte una lista en una tabla.
Table.FromList(list as list, optional splitter as nullable function, optional columns as any, optional default as any, optional extraValues as nullable number)
parámetros de la función:
list: la lista que queremos convertir en tabla.
splitter: Es opcional, aplica una función de división a cada elemento de la lista. En este caso no se requiere dividir los elementos de la lista y usamos la función Splitter.SplitByNothing().
columns: Es opcional, puede contener el número de columnas, una lista de las columnas o un tipo table. En este caso hemos pasado una declaración del tipo table donde el nombre de la columna es Fecha y el tipo de dato que contiene es date.
defaut: Es opcional, valor predeterminado. En este caso no se ha pasado valor por defecto.
extraValues: Es opcional, valores adicionales en caso de error. Si la función de división devuelve más columnas de las que la tabla espera, se puede:
- generar un error (Error)
- ignorar el error (Ignore)
- recopilar en una lista (List)
En el ejemplo se generará un error.
- Agregamos una columna de año a la tabla usando la fórmula:
- Añadimos una columna con el número del mes a la tabla usando la fórmula:
- Adicionamos una columna con el nombre del mes usando la fórmula:
- Añadimos la columna trimestre mediante la expresión:
- Agregamos columna con el numero de la semana del año utilizando la fórmula:
- Adicionamos columna con el día de la semana usando la expresión:
- Añadimos columna con YYMM según la fórmula:
- Creamos un parámetro para almacenar el número del mes fiscal.
- Agregamos una columna con el número de mes fiscal:
- Añadimos una columna con el año fiscal
- Adicionamos la columna FYYMM con el año y mes fiscal.
ColumnaAño = Table.AddColumn(TablaFechas,"Año", each Date.Year([Fecha]), Int64.Type)
ColumnaNroMes = Table.AddColumn(ColumnaAño,"Nro. Mes", each Date.Month([Fecha]), Int64.Type)
ColumnaMes = Table.AddColumn(ColumnaNroMes,"Mes", each Date.MonthName([Fecha]), type text)
ColumnaTrimestre = Table.AddColumn(ColumnaMes,"Trimestre", each Text.From(Date.QuarterOfYear([Fecha])) & "T", type text)
ColumnaSemana = Table.AddColumn(ColumnaTrimestre,"Semana", each Date.WeekOfYear([Fecha],Day.Monday), Int64.Type)
ColumnaDiaSemana = Table.AddColumn(ColumnaSemana,"Día Semana", each Date.DayOfWeek([Fecha],Day.Monday),Int64.Type)
ColumnaYYMM = Table.AddColumn(ColumnaDiaSemana, "YYMM", each [Año]*100 + [Nro. Mes], Int64.Type)
ColumnaNroMesFiscal = Table.AddColumn(ColumnaYYMM, "Nro. Mes Fiscal", each if [Nro. Mes] > NroMesFiscal then [Nro. Mes] - NroMesFiscal else [Nro. Mes] + NroMesFiscal, Int64.Type)
ColumnaAñoFiscal = Table.AddColumn(ColumnaNroMesFiscal,"Año Fiscal", each if [Nro. Mes Fiscal] <= NroMesFiscal then [Año] + 1 else [Año], Int64.Type)
ColumnaFYYMM = Table.AddColumn(ColumnaAñoFiscal,"FYYMM", each [Año Fiscal]*100 + [Nro. Mes Fiscal], Int64.Type)
Crear calendario de días festivos
Para los días festivos usaremos la URL de una página que contiene los festivos de España para los años 2018, 2019 y 2020. Tendremos que realizar algunas transformaciones hasta obtener una tabla con el formato requerido.- Creamos una nueva consulta usando el conector Web. La URL del origen es https://www.diafestivo.es/. Extraemos la tabla que contiene los festivos de España.
- Renombramos la consulta como Festivos España.
- Utilizar la primera fila como encabezado será nuestra primera transformación.
- Eliminamos el paso Tipo cambiado.
- A continuación, seleccionamos la columna España y la transformación Anular la dinamización de otras columnas.
- Filtramos las filas que contengan “-“ en la columna Valor.
- Seleccionamos la columna Valor y luego la columna Atributo, en la pestaña Transformar, dentro del grupo Columnas de texto, seleccionamos la opción Combinar columnas. Como separador escogemos Espacio y como nombre de columna escribimos Fecha.
- Cambiamos el tipo de datos de la columna Fecha al tipo Fecha.
- Seleccionamos la columna fecha y eliminamos duplicados.
- Cambiamos el nombre a la columna España por Fiesta.
- Y ya está el calendario de festivos de España.
Añadir festivos a la consulta fCalendario
- Seleccionamos la consulta fCalendario.
- En el Editor avanzado, añadimos un nuevo paso donde combinamos las dos consultas utilizando la fórmula:
- Añadimos un nuevo paso donde expandimos la columna Días festivos:
- Añadimos una columna que devuelva para cada día si es festivo o no teniendo en cuenta si es fin de semana o día de fiesta nacional.
- Ya está listo nuestro calendario.
Consultascombinadas = Table.NestedJoin(ColumnaFYYMM, {"Fecha"}, #"Festivos España", {"Fecha"}, "Días festivos", JoinKind.LeftOuter)
Diasfestivos = Table.ExpandTableColumn(Consultascombinadas, "Días festivos", {"Fiesta"}, {"Fiesta"})
ColumnaLaborable = Table.AddColumn(Diasfestivos, "Festivo", each if [Fiesta] <> null or [Día Semana] = 6 or [Día Semana]=7 then 1 else 0, Int64.Type)
Crear función a partir de la consulta anterior.
- Seleccionamos la consulta fCalendario y a continuación abrimos el Editor avanzado.
- En la primera línea del editor, después de la expresión let realizamos un cambio de línea.
- En la línea 2 del editor declaramos la función mediante la siguiente expresión:
Origen = (FI as date, FF as date)=>
- Realizamos otro cambio de línea y en la línea 3 del editor escribimos la expresión let para indicar el comienzo del cuerpo de la función.
- Después del último paso añadimos otra línea donde escribimos la expresión in y a continuación un nuevo cambio de línea.
- Escribimos Origen y a continuación oprimimos el botón Listo.
- Habremos convertido la consulta en una función.
Crear las cinco consultas correspondientes a cada caso de uso.
Calendario 1: crear un calendario a partir de dos valores de fechas fijos, fecha inicial y fecha final.
- Crear consulta en blanco.
- En la primera línea de la consulta escribir la expresión
= fCalendario(FI,FF)
Hemos llamado a la función que acabamos de crear pasándole como valores los parámetros definidos previamente.
Como resultado se ha obtenido un calendario que va desde el valor de una fecha fija hasta el valor de otra fecha fija.
Calendario 2: crear un calendario con los X últimos años hasta la fecha actual.
- Crear consulta en blanco.
- Asignar el valor de la fecha actual a la variable fecha_final usando la función DateTime.LocalNow()
fecha_final = Date.From(DateTime.LocalNow())
- Crear parámetro con el número de años del calendario.
- Restar a la fecha actual el número de años deseados para encontrar el valor inicial:
fecha_inicial = Date.AddYears(fecha_final,-1*Años),
calendario = fCalendario(fecha_inicial, fecha_final)
Calendario 3: crear un calendario con los X últimos años desde el primer día del primer año hasta el último día del año actual.
- Crear consulta en blanco.
- Asignar el valor de la fecha actual a la variable fecha_final usando la función DateTime.LocalNow()
fecha_final = Date.From(DateTime.LocalNow())
año_final = Date.Year(fecha_final)
ff = Date.From("31/12/" & Text.From(año_final))
fecha_inicial = Date.AddYears(fecha_final,-1*Años),
año_inicial = Date.Year(fecha_inicial)
fi = Date.From("01/01/" & Text.From(año_inicial))
calendario = fCalendario(fi,ff)
Calendario 4: crear un calendario dinámico a partir de una columna de fechas, donde el menor valor será la fecha inicial y el mayor valor la fecha final.
- Crear consulta en blanco.
- Asignar el menor valor de una columna de fechas a la variable fecha_inicial usando la expresión:
fecha_inicial = List.Min(Ventas[OrderDate])
fecha_final = List.Max(Ventas[OrderDate])
calendario = fCalendario(fecha_inicial, fecha_final)
Calendario 5: crear un calendario dinámico a partir de varias columnas de fechas. Seleccionaremos el menor valor entre todas las columnas como valor inicial y el mayor como valor final del calendario.
Primero crearemos una función que devuelva una lista de valores con el mayor y el menor valor de una columna de fechas y luego crearemos el calendario usando la función.
Pasos para crear la función:
- Crear una consulta en blanco.
- En el paso Origen asignar la siguiente expresión para la declaración de la función:
Origen = (tabla as table, columna as text) =>La función contiene dos parámetros una para la tabla y otro para la columna de tipo fecha.
FI = Record.Field (Table.Min(tabla,columna),columna),Esta expresión devuelve la primera fecha de la columna especificada.
FF = Record.Field(Table.Max(tabla,columna),columna),Esta expresión devuelve la última fecha de la columna especificada.
Lista = List.Combine({{FI},{FF}})Esta expresión devuelve una lista que contiene el primer y el ultimo valor de la columna fecha pasada como parámetro a la función.
Pasos para crear el calendario.
Para la tabla que contiene varias columnas de fechas usaremos la tabla FactInternetSales de AdventureWorksDW2017 a la que hemos llamado Ventas. Esta tabla tiene tres columnas de fecha: OrderDate, DueDate y ShipDate. Usaremos la función anterior para buscar el menor y el mayor valor de cada una de estas columnas, combinarlos en una lista y luego seleccionar el menor y el mayor entre estos valores para crear nuestro calendario siguiendo los pasos que se detallan a continuación:
- Creamos una consulta en blanco y abrimos el Editor avanzado.
- Sustituimos el paso Origen por la expresión:
DatesOrder = MinMaxDate(Ventas,"OrderDate"),
DatesDue = MinMaxDate(Ventas,"DueDate"),y
DatesShip = MinMaxDate(Ventas,"ShipDate"),
AllDates = List.Combine({DatesOrder,DatesDue,DatesShip}),
fecha_inicial = List.Min(AllDates)
fecha_final = List.Max(AllDates),
calendario = fCalendario(fecha_inicial, fecha_final)
Conclusiones:
- Podemos crear calendarios utilizando Power Query o funciones DAX. Las dos son muy similares a la hora de crear un calendario.
- En muchos escenarios, los días festivos juegan un papel importante en el análisis de datos. Con Power Query podemos obtener datos de las web en vivo. Esta funcionalidad permite buscar los días festivos en páginas web públicas en vivo y añadirlos a nuestro calendario. Esta funcionalidad no la tiene DAX y es la ventaja fundamental de crear el calendario con M.
El archivo PIBX de este ejemplo está disponible en GitHub.
10 comentarios
Gustavo PizarroMar 26, 2020 - 2:11 pm
Excelente trabajo, aprendí muchísimo, lo voy a utilizar.
Muchas gracias.
Diana AguileraMar 26, 2020 - 4:13 pm
Muchas gracias Gustavo, si necesitas ayuda puedes escribirme.
Carlos CabreraAbr 29, 2020 - 2:42 pm
Muy bueno.
Bien explicado, detallado y útil para cualquier modelo. Tomaremos nota.
Gracias por vuestra aportación.
Diana AguileraAbr 29, 2020 - 6:22 pm
Muchas gracias por tus comentarios Carlos.
David MarquezJul 29, 2020 - 3:06 pm
Excelente, la mejor explicación que he visto sobre alguna duda que he tenido de PBI Querry… Brillante
Diana Aguilera ReynaJul 29, 2020 - 4:30 pm
Hola David, muchas gracias por tu comentario. Me alegra que te haya ayudado.
Saludos,
Diana
AngieJun 22, 2021 - 12:37 am
Muchas gracias por toda esta valiosísima información, como dicen en los comentarios anteriores, nunca he encontrado algo tan bien explicado y detallado para este tema. Un aporte extraordinario. Gracias por tu tiempo, dedicación y por compartirlo con otros usuarios.
Saludos,
Angie
Diana Aguilera ReynaJun 22, 2021 - 9:35 pm
Hola Angie,
Gracias por tus comentarios!
Saludos.
Jose Rafael PinoEne 21, 2022 - 3:38 pm
Excelente post , que no pierde vigencia. Tengo tiempo usando partes de tu script , dado que prefiero usar power query para mis calendarios.
Una pregunta ? sabes como colocar una funcion como esta como nativa , para no tener que estar copy/paste. Ya he visto par de modelos que llaman una funcion calnedario desde el editor de consultas . Estoy viendo algunos videos sobre como se hace , pero me da error , seguire intentando. Saludos
Diana Aguilera ReynaEne 23, 2022 - 9:59 pm
Hola Jose Rafael, muchas gracias por tus comentarios!
En cuanto a guardar la función en la librería de Power Query necesitas hacer algunos cambios a la función:
– Cambiar la función List.Dates para generar la lista con las fechas, hay que hacerlo de otra forma.
– Al utilizar la función Table.FromList no asigna correctamente el tipo de dato a la columna de fecha. Debes añadir a continuación de este paso otro donde hagas el cambio de tipo de datos de la columna Fecha.
– Si vas a crear las columnas para el calendario fiscal debes añadir otro parámetro a la función. El parámetro donde se indica el número de mes donde empieza el calendario fiscal.
He creado el archivo MyLibrary.pqx, que puedes descargar de GITHUB y probarlo en tu modelo.
Saludos,
Diana.