Trabajando con Datetime y Time

Photo by Pixabay on Pexels.com

Comenzamos…

Dejamos ya importadas las librerías que vamos a necesitar:

import datetime
import time
import numpy as np
from datetime import datetime,timedelta
from zoneinfo import ZoneInfo, available_timezones
import calendar
%matplotlib inline
import pandas as pd
import sys

A raiz de la actualización del código que soporta al @elgalloaurora he estado jugando y probando con el tratamiento y gestión de horas con sus diferentes versiones “naive” y “no naive” y el cambio entre UTC, referenciadas a diferentes timezones y en modo segundos desde un determinado epoch. He escrito este pqueño doc para que me sirva de memoria para futuros trabajos

Vemos cosas básicas, como :

Horal actual local

datetime.now(ZoneInfo('Europe/Madrid'))
datetime.datetime(2024, 5, 26, 11, 50, 47, 425942, tzinfo=zoneinfo.ZoneInfo(key='Europe/Madrid'))
datetime.now(ZoneInfo('US/Hawaii'))
datetime.datetime(2024, 5, 25, 23, 50, 47, 666428, tzinfo=zoneinfo.ZoneInfo(key='US/Hawaii'))

y aquí se puede encontrar todas las zonas disponibles:

available_timezones()
{'Africa/Abidjan',
 'Africa/Accra',
 'Africa/Addis_Ababa',
 'Africa/Algiers',
 'Africa/Asmara',
 'Africa/Asmera',
 'Africa/Bamako',
 'Africa/Bangui',
 'Africa/Banjul',
 'Africa/Bissau',
 'Africa/Blantyre',
 'Africa/Brazzaville',
 'Africa/Bujumbura',
 'Africa/Cairo',
 'Africa/Casablanca',
 'Africa/Ceuta',
 'Africa/Conakry',
 .....
 'US/Samoa',
 'UTC',
 'Universal',
 'W-SU',
 'WET',
 'Zulu',
 'build/etc/localtime'}

Otra manera de conseguir la hora actual en otra zona horaría es con astimezone

datetime.now(ZoneInfo('Europe/Madrid')).astimezone(ZoneInfo('Europe/Lisbon'))
datetime.datetime(2024, 5, 26, 10, 50, 49, 27417, tzinfo=zoneinfo.ZoneInfo(key='Europe/Lisbon'))

o directamente:

datetime.now().astimezone(ZoneInfo('Europe/Lisbon'))
datetime.datetime(2024, 5, 26, 10, 50, 49, 474078, tzinfo=zoneinfo.ZoneInfo(key='Europe/Lisbon'))

Como caso especial tenemos la hora UTC, muy útil pues es la que nos devuelven, y aceptan, muchas librerías:

datetime.now(ZoneInfo('UTC'))
datetime.datetime(2024, 5, 26, 9, 50, 50, 453552, tzinfo=zoneinfo.ZoneInfo(key='UTC'))

Hora naive y no naive:

fecha = datetime(year=2024,month=5,day=1,hour=10,minute=10)

Esa hora no tiene asociao ningún timezone, y lo podemos confirmar llamando a la función tzinfo:

fecha.tzinfo

Sin embargo esta fecha sí está asociada, en particular a Lisboa:

fecha_lisboa = datetime(year=2024,month=5,day=1,hour=10,minute=10,tzinfo=ZoneInfo(key='Europe/Lisbon'))

en efecto:

fecha_lisboa.tzinfo
zoneinfo.ZoneInfo(key='Europe/Lisbon')

Y que impacto tiene esa diferencia?: por ejemplo, que no puedes restarlas. No puedes calcular la diferencia en tiempo entre una fecha naive y no naive

fecha-fecha_lisboa
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

Cell In[114], line 1
----> 1 fecha-fecha_lisboa


TypeError: can't subtract offset-naive and offset-aware datetimes

Sin embargo esto si funciona al estar comparando dos naives :

fecha_1 = datetime(year=2024,month=5,day=1,hour=10,minute=10,tzinfo= ZoneInfo('Europe/Lisbon'))
fecha_2 = datetime(year=2024,month=5,day=1,hour=11,minute=10,tzinfo= ZoneInfo('Europe/Madrid'))

qué resultado dará la resta anterior?

(fecha_1-fecha_2).seconds
0

efectivamente, la 11:10 hora local en Madrid corresponden con las 11:10 en Lisboa

Horas medidas en segundos

Este resultado nos da los segundos desde un epoch en particular. Y qué es eso de un epoch?..es una referencia, tal como se define en la documentacion oficial de la librería time : > The epoch is the point where the time starts, the return value of time.gmtime(0). It is January 1, 1970, 00:00:00 (UTC) on all platforms.

time.time()
1716717063.091218

y como conseguimos esa referencia?. La conseguimos con la ordent:

time.gmtime(0)
time.struct_time(tm_year=1970, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=1, tm_isdst=0)

que podemos convertir a formato ´datetime`

datetime.fromtimestamp(time.mktime(time.gmtime(0)))
datetime.datetime(1970, 1, 1, 0, 0)

ese time.time() nos da un resultado en hora UTC. Para comprobarlo vamos a usar la función gmtime

time.gmtime(time.time())
time.struct_time(tm_year=2024, tm_mon=5, tm_mday=26, tm_hour=9, tm_min=51, tm_sec=5, tm_wday=6, tm_yday=147, tm_isdst=0)

y para conseguirla en hora actual:

time.localtime(time.time())
time.struct_time(tm_year=2024, tm_mon=5, tm_mday=26, tm_hour=11, tm_min=51, tm_sec=5, tm_wday=6, tm_yday=147, tm_isdst=1)

Y podemos conseguir esa hora en formato datetime tal como lo veíamos anteriormente

datetime.fromtimestamp(time.time()) #ojo...en local, aunque no naive
datetime.datetime(2024, 5, 26, 11, 51, 7, 303250)
datetime.fromtimestamp(time.time()).replace(tzinfo=ZoneInfo('Europe/Madrid'))
datetime.datetime(2024, 5, 26, 11, 51, 7, 536789, tzinfo=zoneinfo.ZoneInfo(key='Europe/Madrid'))

ojo, que replace tan solo cambia la timezone pero no recalcula la hora de acuerdo a ese cambio.

datetime.fromtimestamp(time.time()).replace(tzinfo=ZoneInfo('Europe/Madrid')).replace(tzinfo=ZoneInfo('Europe/Lisbon'))
datetime.datetime(2024, 5, 26, 11, 51, 7, 950463, tzinfo=zoneinfo.ZoneInfo(key='Europe/Lisbon'))

y para generar una hora en segundos desde una estructurada:

time.time()
1716717068.888389
time.gmtime(time.time())
time.struct_time(tm_year=2024, tm_mon=5, tm_mday=26, tm_hour=9, tm_min=51, tm_sec=9, tm_wday=6, tm_yday=147, tm_isdst=0)
calendar.timegm(time.gmtime(time.time()))
1716717069

que es dentro de una hora (los 3600 segundos sumados)

Y para pasar a un formato no naive desde segundos:

datetime.fromtimestamp(time.time()+3600,tz=ZoneInfo('Europe/Madrid'))
datetime.datetime(2024, 5, 26, 12, 51, 10, 228910, tzinfo=zoneinfo.ZoneInfo(key='Europe/Madrid'))
datetime.fromtimestamp(time.time(),tz=ZoneInfo('Europe/Lisbon'))
datetime.datetime(2024, 5, 26, 10, 51, 10, 435502, tzinfo=zoneinfo.ZoneInfo(key='Europe/Lisbon'))

Así como resumen, y replicando el cuadro de la documentación de la librería:

  • Cambio de segundos desde epoch a struc_time en UTC : gmtime()

  • Cambio de segundos desde epoch a struct_time en LocatTime : localtime()

  • Cambio de struct_time en UTC a segundos : calendar.timegm()

  • Cambio de struct_time local time a segundos: mktime()

    y añado dos más:

  • Cambo de datetime a time_struct : datetime.timetuple()

  • Cambio de time_struct a datetime : datetime.fromtimestamp(mktime(time_struc))


¿Cómo tuitear desde Python?

¿Para que vale eso?

Desde mi cuenta de twitter @walyt publico diariamente diversos gráficos con información relevante del Mercado Eléctrico: precios del spot del día, como se comporta la generación, evolución precios, previsiones energía solar etc…Estos tuits los publico de manera «automatizada» por medio de scripts en Python residentes en una máquina virtual en Google Cloud. Por medio de un crontab planifico a lo largo del día esa publicación de información. Así aseguras una publicación de manera regular, y que la información de base está siempre disponible.

Me di cuenta el fin de semana pasado de que hacía varios días que no salía ningun nuevo tuit. Alarma!. Tras chequear y cacharrear un poco me di cuenta del algún cambio en la API de twitter, que hacía que el procedimiento que estaba usando no era válido. Hasta este fin de semana no he tenido unas horas para arreglarlo. Y parece que ya lo tengo. Para ayudar a quien le pase algo parecido o para quien necesite este código he preparado este post.

Qué necesito?

Lo primero que se necesita es obvio: una cuenta de twitter!!!

Venga, que me imagino que la tenéis. Lo segundo es dar de alta esa cuenta en el portal de desarrollo. Nos encontramos aquí tres modalidades :

  • Free: que te permite acceso gratuito, aunque con alguna limitación, a las APIs de Twitter. Te permite publicar hasta 1.500 tuits al mes, y realizar 50 llamadas (queries) al día. Esta es la modalidad con la que estoy subscrito.
  • Basic : con un coste de 100$/mes, dos entornos de aplicación, descarga de 10.000 tuis al mes, 1.667 peticiones cada 24 horas…
  • Pro : con un coste de 5.000€/mes se multiplican, obviamente, las capacidades: 3 entornos de aplicaciones, puedes descargar hasta un millón de tuits al mes, publicar 100 tuits cada 15 min ó 10.000 al mes. Además de muchos endpoints para la gestión total de una cuenta

…y dar de alta la APP

A continuación daremos de alta la entorno y una aplicación, simplemente hay que añadir una breve descripción…Así me aparece:

y solo queda ir a la pestaña de «Keys and tokens», en la que deberemos generar cinco claves:

  • Bearer Token que identifica a la App
  • API Key y API Secret, que tal como indica : «Think of these as the user name and password that represents your App when making API requests. While your Secret will remain permanently hidden, you can always view the last 6 characters of your API Key
  • Access Token and Access Secret, que de igaul manera describe como : «An Access Token and Secret are user-specific credentials used to authenticate OAuth 1.0a API requests. They specify the Twitter account the request is made on behalf of.«

copia, de manera segura, esas claves y ya estaríamos listos para empezar con Python

Un poco de código

#importamos la librería
import tweepy

#definimos las variables que nos identifican junto con nuestra app
api_key = 'tus valores'
api_key_secret = 'tus valores'
access_token = 'tus valores'
access_token_secret = 'tus valores'

bearer_token = 'tus valores'

#creamos un cliente tweepy
cliente = tweepy.Client(bearer_token=bearer_token,
consumer_key=api_key,
consumer_secret=api_key_secret,
access_token=access_token,
access_token_secret=access_token_secret)

#llamamos a la query de twitter para publicarlo
cliente.create_tweet(text='Hola a todos!')

Y ya debería tener el tuit en el timeline.

Si, como es mi caso, quiere publicar el tuit con un gráfico se deberá subir previamente ese gráfico con el API v1, recoger el id, y añadir ese media_id en la llamada a la query. Queda el código tal que así :

#importamos la librería
import tweepy

#definimos las variables que nos identifican junto con nuestra app
api_key = 'tus valores'
api_key_secret = 'tus valores'
access_token = 'tus valores'
access_token_secret = 'tus valores'
bearer_token = 'tus valores'

#hacemos una llamada al apiv1 para subir el gráfico
auth = tweepy.OAuth1UserHandler(api_key, api_key_secret)
auth.set_access_token(access_token, access_token_secret)
api = tweepy.API(auth)
media = api.media_upload(filename='media_horaria.png')
media_id = media.media_id

#creamos un cliente tweepy
cliente = tweepy.Client(bearer_token=bearer_token,
consumer_key=api_key,
consumer_secret=api_key_secret,
access_token=access_token,
access_token_secret=access_token_secret)

#hacemos la llamada para publicar, pero en esta ocasión con media
cliente.create_tweet(text='otra prueba, pero esta vez con gráfico',media_ids=[media_id])

Próximos pasos

Me he quedado con dos acciones :

  • No usar una librería externa y hacer llamadas directamente al API de twitter
  • Buscar un modo de subir el fichero con la v2 y no con la v1. Sospecho que en cualquier momento pueden meter alguna nueva limitación a la v1, y tener que rehacer los scripts de nuevo
  • ..y una tercera : tengo que añadir logs a los scripts para que me alerten si ha habido algún problema. Y así evitar estos 10 días sin publicar nada

Gracias a todos por los comentarios en twitter y los ánimos para seguir con estas tareas.

Descarga de datos del mercado eléctrico.

Jugar con los datos abiertos que se proporcionan en las diferentes fuentes de información de este mercado es una buena manera de intentar entenderlo. En mi cuenta de twitter publico diariamente gráficos generados con datos descargados desde estas fuentes. Es un mercado ciertamente complejo, con una regulación extensa, y con mucho ruido mediático. Todo esto facilita la opacidad y dificultad de entender este mercado, que por otra parte se lleva, y más con la subida de precios de estos tiempos, una parte material del presupuesto de servicios básicos de las familias. Mi intención, publicando estas líneas de código (por otra parte extremadamente simple) es ayudar un poco en hacerlo más transparente.

¿Como lo he organizado?

He preparado un notebook de Jupyter mostrando una serie de funciones, en Python, para facilitar la descarga de datos desde fuentes relacionadas con el mercado eléctrico:

He preparado cinco funciones:

  • catalogo_esios(token): que permite bajar el catálogo completo de la api de esios, para, posteriorment, buscar de manera fácil los identificadores que necesitamos para nuestro trabajo
  • download_esios : que permite bajar datos de cualquier indentificador, entre dos fechas determinadas, para posteriormente trabajar sobre ellos.
  • download_ree : idem que el caso anterir pero sobre apidatos de REE
  • download_gas : que nos permite bajar desde https://mibgas.es el precio de gas GDAES del día siguiente
  • downlaod_gas_rd: que nos permite bajar desde https://mibgas.es el precio de gas correspondiente al mecanismo de compensación del RD10/2022

A modo de caso práctico, he añadido un ejemplo de descarga de datos junto con un gráfico para mostrarlos visualmente. He procurado elegir tipos diferentes de gráficos para cada gráfico por si es de ayuda.

Aquí pueden encontrar el notebook.

Me alegraré mucho si alguien lo encuentra de utilidad…

Inferencia Bayesiana vs Frecuentista

Introducción..un poco de teoría

En este trabajo vamos a estudiar las dos aproximaciones más conocidas en los métodos de inferencia estadística: el método bayesiano y el frecuentista. Un búsqueda rápida en Google confirma el gran número de interesantes discusiones al respecto. Personalmente me decanto por el Bayesiano, al final de este trabajo explicaré por qué.

Photo by Kaboompics .com on Pexels.com
Leer más »

Análisis de la variación del cambio Euro Dolar. Riesgo de cambio.

Photo by Karolina Grabowska on Pexels.com

Vamos a preparar un script que nos permita evaluar las variaciones que hay en los cambios de moneda, y nos de una idea de que riesgos estamos corriendo cuando fijamos los cambios. Las series históricas las sacamos de la libreria https://pypi.org/project/investpy/ (construída por Álvaro Bartolomé del Canto @alvarob96 at GitHub) que obtiene los datos de la web de información financiera http://www.investing.com

Leer más »

La estadística de los sondeos

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
view raw sondeos.ipynb hosted with ❤ by GitHub

Gestión de followers de Twitter con Python

Seguimiento de Followers

Existen varias herramientas para gestionar cuentas de twitter que te permiten controlar el movimiento de seguidores: quien te sigue, te deja de seguir, etc…
Para practicar un poco construyendo apps me he puesto a codificar un sencillo programa que te gestione los followers de tu cuenta sin necesidad de instalar las herramientas mencionadas. Este código puede ser el embrión sobre el que añadir más funcionalidades. Ahí queda trabajo pendiente.

Leer más »

Teoremas «límite» de la Estadística: Ley de los Grandes Números (LLN: Large Numbers Law) (1)

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
view raw lln.ipynb hosted with ❤ by GitHub