¡Jueves de post y video! Aprovechando un par de preguntas que me lanzasteis varios de vosotros sobre como calcular la semana bajo la norma ISO:8601 en el Global Power Platform Bootcamp, he querido realizar un post paso a paso para el cálculo de la semana ISO en Power Query y poder añadirlo a nuestra dimensión tiempo de nuestro dataflow.
Lo primero que debemos saber es que nos dice la norma ISO: 8601y es lo siguiente:
La norma ISO 8601 establece que una semana inicia el día lunes y termina el día domingo. Además, esta norma indica que se considera la primera semana de un año aquella que contiene el primer jueves de dicho año, o lo que es lo mismo, aquella que contiene el día 4 de enero.
¿Y esto que quiere decir? Bien, que el primer día de la semana es el lunes (1) y el último día es el domingo (7). Las semanas tienen 7 días y y su numeración siempre empiezan en 1. Además, la primera semana del año, la semana 1, es aquella contiene el primer jueves de dicho año, o lo que es lo mismo, el 4 de enero.
Con estos criterios, podemos calcular la semana ISO, ¿no creéis? Vamos a ello.
Lo primero que tenemos que hacer es calcular la semana del año, ¿y cómo calculamos la semana de un año? Dividiendo el número de días del año (365 o 366) entre 7 que son los días de la semana (de lunes a domingo). En función de si el año es bisiesto o no, nos va devolver un valor de 52,14 (365/7) o 52,28 (366/7).
Pero esto no es lo que nosotros necesitamos saber, nosotros lo que necesitamos saber es a qué semana corresponde la columna fecha de nuestra dimensión calendario:
¿Y cómo lo hacemos? De la siguiente manera:
Lo primero que tenemos que hacer es calcular es el número de día del año que corresponde la fecha en cuestión, es decir, del 1 al 365 o 366 en función de si es bisiesto o no. Para ello usamos la función Date.DayOfYear:
DiaAño = Date.DayOfYear([Fecha])
Una vez obtenido en número del día, calculamos el número del día dentro de la semana. Acordaros que la norma ISO indica que el primer día de la semana es Lunes y el último el Domingo, por ello el lunes tiene que tener el valor 1 y el domingo el valor 7. Para ello usamos la función Date.DayOfWeek y le indicamos que el primer día de la semana es el lunes y le sumamos 1 para su valor sea es, y nos queda:
DiaSemana = (Date.DayOfWeek([Fecha], Day.Monday)+1)
A continuación, restamos al número día que el corresponde de la fecha en cuestión, el número de la semana. Y le sumamos 10, así evitamos por ejemplo los posibles negativos y el resultado obtenido lo dividimos entre 7 que es el número de días de una semana. Y de esta manera ya tenemos nuestra semana calculada:
Semana = ([DiaAño]- [DiaSemana]+10)/7
Pero como las semanas en mi pueblo son números enteros, lo que debemos hacer es redondear a la baja para obtener el número de la semana. Para ello:
Semana = Number.RoundDown= ([DiaAño]- [DiaSemana]+10)/7)
Y de esta manera ya tenemos nuestras semanas ¿no? Pero…
Aquí podemos obtener un resultado igual a 0 o a 53 entre otros, ¿qué significa que obtengamos un 0 o un 53? ¿Qué hemos hecho mal el cálculo? No, significa que la fecha en cuestión pertenece al año anterior o el año en cuestión tiene 53 semanas.
Empecemos por la semana 0. Al ser 0, pertenece al año anterior y puede ser semana 52 o semana 53. Para calcularlo lo que debemos obtener el número de días del año anterior, y restarle el día de la semana a la que pertenece ese día, es decir, el último día del año anterior, agregarle 10 y dividirlo entre el número de días de una semana, o lo que es lo mismo entre 7. Exactamente lo mismo que para el cálculo de la semana, pero para el año anterior.
Para ello obtenemos fecha final del año anterior, es por eso
que le restamos 1 al año obtenido de nuestra columna Fecha. Y con la función
#date, lo que hacemos es crear un valor de fecha a partir de números enteros
que representan el año (obtenido con la función Date.Year), mes y día los
introducimos manualmente:
FinalAñoAnteriorFecha=#date(Date.Year([Fecha])-1,12,31))
Calculamos el número de días que tiene el año anterior, a nuestra columna Fecha, o lo que es lo mismo, el número de días que tiene el año de la columna FinalAñoAnteriorFecha:
NumeroDiasAñoAnterior= Date.DayOfYear(FinalAñoAnteriorFecha)
Ahora tenemos que obtener el día de la semana de la fecha fin del año anterior, es decir, de la columna FinalAñoAnteriorFecha. Cómo queremos que empiece el lunes en 1, le sumamos 1 como hasta ahora.:
DiaSemanaFinalAñoAnterior = (Date.DayOfWeek([FinAñoAnteriorFecha], Day.Monday)+1)
A continuación, como hemos hecho con la semana del año actual, le sumamos 10 y dividimos entre 7. Y lo de siempre, los días tienen que ser números enteros, por lo que redondeamos hacia abajo.
SemanaAñoAnterior = Number.RoundDown((([NumeroDiasAñoAnterior]-[DiaSemanaFinalAñoAnterior])+10)/7)
Y como podemos ver, hay fechas que pueden pertenecer a la semana 52 o semana 53 del año anterior.
Hasta aquí hemos cubierto todas las semana exceptuando la 53, y la semana 0 que puede ser la semana 52 o 53. ¿Bien?
La otra posibildiad es que la semana calculada sea igual a 53, entonces debemos comprobar que realmente si pertenece o no a la semana 1 del año. ¿Cómo sabemos si pertenece al año actual? Como indica la norma ISO:8601 si la semana es igual a 53 y el día de la semana de la fecha fin del año es menor a 4 (jueves) entonces es semana 1, sino es semana 53. Pues vamos a ello. Calculamos lo primero la fecha fin del año, para ello:
Ahora lo que debemos de obtener es el día de la semana del final del año para ver si es inferior a 4. De ser así, según la norma ISO es semana 1, sino, será semana 53. Por lo que creamos otra columna nueva pero esta vez al introducir la comparativa, le indicamos que la columna es de tipo True/False:
DiaSemanaFinAñoFecha = Date.DayOfWeek([FinalAñoFecha], Day.Monday)+1<4
Ahora ya, tenemos todas las variantes posibles para crear la columna con la semana ISO. Para ello, creamos una nueva columna personalizada en la que escribimos la siguiente condición:
if [Semana_]= 0
then [SemanaAñoAnterior]
else if ([Semana_]= 53 and [DiaSemanaFinAñoFecha]=true)
then 1
else
[Semana_]
Si obtenemos ahora la columna de semana mediante la interfaz de usuario de Power Query, vamos a ver las diferencias a la hora de numerar las columnas:
Y hasta aquí es todo, lo único que me queda por añadir es que para este ejemplo, he realizado todos los pasos muy despedazados y he creado columnas innecesarias a modo ilustrativo, pero que sepáis que se puede o se debe agrupar todo en un único paso quedando el código M de la siguiente manera:
if Number.RoundDown((Date.DayOfYear([Fecha])-(Date.DayOfWeek([Fecha], Day.Monday)+1)+10)/7)=0
then
Number.RoundDown((Date.DayOfYear(#date(Date.Year([Fecha])-1,12,31))-(Date.DayOfWeek(#date(Date.Year([Fecha])-1,12,31), Day.Monday)+1)+10)/7)
else if
(Number.RoundDown((Date.DayOfYear([Fecha])-(Date.DayOfWeek([Fecha], Day.Monday)+1)+10)/7)=53 and (Date.DayOfWeek(#date(Date.Year([Fecha]),12,31), Day.Monday)+1<4))
then 1
else
Number.RoundDown((Date.DayOfYear([Fecha])-(Date.DayOfWeek([Fecha], Day.Monday)+1)+10)/7)
Y con esto ya tenemos nuestra columna de semana bajo norma ISO:8601. Espero que os guste y os sea útil. Os dejo aquí el video:
¡Nos vemos en los datos!