Encontrar patrones temporales en publicaciones de Twitter Análisis exploratorio de datos con Python (Parte 2)

Análisis exploratorio de datos con Python para encontrar patrones temporales en publicaciones de Twitter (Parte 2)

Análisis del comportamiento de los usuarios con Python y Pandas

Ejemplo de cronograma de usuario, Imagen del autor

En la primera parte de este artículo, analicé las marcas de tiempo de alrededor de 70,000 publicaciones de Twitter y obtuve algunos resultados interesantes; por ejemplo, fue posible detectar bots o usuarios que publicaban mensajes desde cuentas clonadas. Pero no pude obtener una hora precisa del mensaje; al menos para una cuenta gratuita, la respuesta de la API de Twitter no tiene una zona horaria y todos los mensajes tienen hora UTC. Millones de personas están utilizando redes sociales hoy en día y el análisis del comportamiento de los usuarios no solo es interesante, sino que también puede ser importante para estudios sociológicos o psicológicos. Por ejemplo, puede ser interesante descubrir si las personas están publicando más mensajes por la tarde, por la noche o durante el día, pero sin tener una hora adecuada, es imposible saberlo. Finalmente, pude encontrar una solución alternativa que funciona bien, incluso con las limitaciones de una API gratuita.

En este artículo, mostraré todo el flujo de trabajo, desde la recopilación de datos hasta el análisis utilizando Python y Pandas.

Metodología

Nuestro flujo de procesamiento de datos consistirá en varios pasos:

  • Recopilación de datos utilizando la biblioteca Tweepy.
  • Carga de datos y obtención de información básica.
  • Transformación de datos. Agruparemos los datos por usuario y encontraremos métricas específicas útiles para el análisis.
  • Análisis de los resultados.

Empecemos.

1. Recopilación de datos

Como se escribió en la parte anterior, no podemos obtener una zona horaria adecuada para los mensajes de Twitter; todos los mensajes devueltos por la API de Twitter tienen hora UTC. Como solución alternativa, decidí probar tres enfoques:

  • Intenté obtener todos los mensajes utilizando la máscara “*” y analizar el campo de “ubicación” de cada mensaje. No todos los usuarios especificaron la ubicación en Twitter, pero un número bastante grande lo hizo. La idea era buena, pero prácticamente no funcionó. Twitter es una gran red social; genera una enorme cantidad de datos y recopilar todos los tweets incluso durante una semana es irrealista. La cantidad de miles de mensajes por segundo no solo es demasiado grande para procesar en una PC común, sino que también estará más allá de las limitaciones de la cuenta gratuita de desarrollador de Twitter.
  • Puedo usar un nombre de ciudad como solicitud; por ejemplo, puedo buscar todos los tweets con el hashtag “#Berlin”. Luego sería fácil filtrar los usuarios que tienen “Alemania” como ubicación y para Alemania, conocemos la zona horaria. Esta idea funciona, pero el problema es que los resultados pueden estar sesgados. Por ejemplo, los mensajes con el hashtag “#Berlin” pueden ser publicados por personas interesadas en política o por fanáticos del deporte. Pero en general, este enfoque es interesante; con diferentes consultas de búsqueda, puede ser posible llegar a diferentes tipos de audiencias.
  • Finalmente, encontré una solución que funciona bien para mí. Decidí obtener todos los mensajes en un idioma específico especificando la máscara “*” y un código de idioma. Obviamente, esto no funcionará para el inglés, pero hay muchos países en el mundo que son lo suficientemente pequeños geográficamente como para determinar fácilmente la zona horaria de sus ciudadanos. Seleccioné el idioma holandés porque el número de hablantes de holandés en el mundo no es tan grande; este idioma se usa principalmente en los Países Bajos y en Bélgica, y ambos países tienen la misma zona horaria. Algunas personas pueden vivir en el extranjero y también hay hablantes nativos de holandés en Surinam y Curazao, pero esos números no son tan grandes.

La recopilación de datos en sí es sencilla. El código ya se usó en la primera parte; solo especificé “*” como máscara de consulta y “nl” como código de idioma. Una API gratuita de Twitter tiene una limitación de 7 días para obtener datos históricos. Pero prácticamente, resultó que la paginación tiene un límite de alrededor de 100,000 mensajes. ¿Es mucho? En realidad, no. La mayoría de las personas probablemente nunca se dan cuenta de cuántos mensajes se publican en las redes sociales. ¡Solo hay alrededor de 25 millones de personas que hablan holandés en el mundo! ¡Y 100,000 es el número de mensajes que estas personas publican en Twitter en solo 3 horas! En la práctica, necesitaba ejecutar el código al menos cada 2 horas para obtener todos los tweets.

Recopilar datos cada dos horas no es un problema; se puede hacer fácilmente en la nube, pero como una solución gratuita, simplemente tomé mi Raspberry Pi:

Raspberry Pi 4, Fuente de imagen https://en.wikipedia.org/wiki/Raspberry_Pi

La Raspberry Pi es una pequeña computadora Linux del tamaño de una tarjeta de crédito con 1 a 8 GB de RAM y una CPU de 1 a 2 GHz. Especificaciones que son absolutamente suficientes para nuestra tarea, y también es agradable que la Raspberry Pi no tenga enfriadores, no produzca ruido y tenga solo 2 a 5 W de consumo de energía. Entonces, es una elección perfecta para ejecutar un código durante una o dos semanas.

Modifiqué ligeramente el script de Python para que pudiera hacer solicitudes cada 2 horas, y también agregué una marca de tiempo al nombre de cada archivo CSV. Después de hacer el inicio de sesión SSH en la Raspberry Pi, pude ejecutar este script en segundo plano usando el comando “nohup” de Linux:

nohup python3 twit_grabs.py >/dev/null 2>&1 &

De forma predeterminada, “nohup” guarda la salida de la consola en el archivo “nohup.out”. Este archivo puede ser grande, por lo que uso el reenvío a “/dev/null” para evitar esto. Otra solución como Cron también se puede usar, pero este comando simple es suficiente para esta tarea.

El proceso se ejecuta en segundo plano, por lo que no vemos nada en la pantalla, pero podemos ver el registro en tiempo real usando el comando “tail” (aquí “20230601220000” es el nombre del archivo actual):

tail -f -n 50 tweets_20230601220000.csv

Obtener tweets en la consola se ve así:

Recopilación de mensajes de Twitter, Imagen por el autor

Cuando sea necesario, podemos copiar nuevos registros de la Raspberry Pi usando el comando “scp”:

scp pi@raspberrypi:/home/pi/Documents/Twitter/tweets_20230601220000.csv .

Aquí, “/home/pi/Documents/…” es una ruta remota en la Raspberry Pi, y “.” es la carpeta actual en una PC de escritorio, donde se deben copiar los archivos CSV.

En mi caso, mantuve la Raspberry Pi funcionando durante unos 10 días, lo que fue suficiente para recopilar algunos datos. Pero en general, cuanto más tiempo, mejor. Durante la preparación de datos para la parte anterior del artículo, vi suficientes usuarios que estaban publicando en Twitter solo una vez por semana; obviamente, se necesitarán intervalos más largos para ver patrones en el comportamiento de esos usuarios.

2. Cargando los datos

El script de Python estaba obteniendo nuevos mensajes de Twitter cada 2 horas, y como salida, se generaron muchos archivos CSV. Podemos cargar todos los archivos en Pandas y combinarlos en un conjunto de datos:

df_tweets = []files = glob.glob("data/*.csv")for file_name in files:    df_tweets.append(pd.read_csv(file_name, sep=';',                                  usecols=['id', 'created_at', 'user_name', 'user_location', 'full_text'],                                  parse_dates=["created_at"],                                  lineterminator='\n', quoting=csv.QUOTE_NONE))df = pd.concat(df_tweets).drop_duplicates('id').sort_values(by=['id'], ascending=True)

El código es sencillo. Cargo cada archivo en el marco de datos, luego combino todos los marcos de datos usando pd.concat. Los intervalos de tiempo se superponen; para evitar tener registros duplicados, uso el método drop_duplicates.

Veamos qué tipo de datos tenemos:

display(df)

El resultado se ve así:

Marco de datos con todos los mensajes, Imagen por el autor

El texto y los ids de los mensajes en realidad no son importantes; para el análisis, solo necesitaremos el campo “created_at”. Para facilitar el procesamiento posterior, extrayamos la fecha, la hora y la hora del día como columnas separadas. También podemos agregar un desplazamiento horario a todos los registros:

tz_offset_hours = 2def update_timezone(t_utc: np.datetime64):    """ Añade la zona horaria a la hora UTC """    return (t_utc + np.timedelta64(tz_offset_hours, 'h')).tz_convert(None)def get_time(dt: datetime.datetime):    """ Obtiene la hora en formato HHMM a partir de la fecha """    return dt.time().replace(        second=0,         microsecond=0)        def get_date(dt: datetime.datetime):    """ Obtiene la fecha de la fecha """    return dt.date()def get_datetime_hhmm(dt: datetime.datetime):    """ Obtiene la fecha y la hora en formato HHMM """    return dt.to_pydatetime().replace(second=0, microsecond=0)def get_hour(dt: datetime.datetime):    """ Obtiene la hora de la fecha """    return dt.hourdf["time_local"] = df['created_at'].map(update_timezone)df["datetime_hhmm"] = df['time_local'].map(get_datetime_hhmm)df["date"] = df['time_local'].map(get_date)df["time"] = df['time_local'].map(get_time)df["hour"] = df['time_local'].map(get_hour)# Opcionalmente, podemos seleccionar solo varios díasdf = df[(df['date'] >= datetime.date(2023, 5, 30)) & (df['date'] <= datetime.date(2023, 5, 31))].sort_values(by=['id'], ascending=True)# Mostrardisplay(df)

El resultado se ve así:

Dataframe con columnas agregadas, Imagen de autor

La carga de datos está lista. Veamos cómo se ve la información.

3. Información general

Este artículo tiene como objetivo analizar los patrones en el dominio “tiempo”. Como calentamiento, veamos todos los mensajes en una sola línea de tiempo. Para dibujar todos los gráficos en el artículo, utilizaré la librería Bokeh:

from bokeh.io import show, output_notebookfrom bokeh.plotting import figurefrom bokeh.models import ColumnDataSourcefrom bokeh.models import SingleIntervalTicker, LinearAxisfrom bokeh.transform import factor_cmap, factor_mark, linear_cmapfrom bokeh.palettes import *output_notebook()def draw_summary_timeline(df_in: pd.DataFrame):    """ Agrupa todos los mensajes por tiempo y dibuja la línea de tiempo """    print("Todos los mensajes:", df_in.shape[0])    users_total = df_in['user_name'].unique().shape[0]    print("Todos los usuarios:", users_total)    days_total = df_in['date'].unique().shape[0]    print("Total de días:", days_total)    print()    gr_messages = df_in.groupby(['datetime_hhmm'], as_index=False).size() # .sort_values(by=['size'], ascending=False)    gr_messages["msg_per_sec"] = gr_messages['size'].div(60)    datetime_hhmm = gr_messages['datetime_hhmm']    amount = gr_messages['msg_per_sec']        palette = RdYlBu11    p = figure(x_axis_type='datetime', width=2200, height=500,                title="Mensajes por segundo")    p.vbar(x=datetime_hhmm, top=amount, width=datetime.timedelta(seconds=50), line_color=palette[0])    p.xaxis[0].ticker.desired_num_ticks = 30    p.xgrid.grid_line_color = None    show(p)     draw_summary_timeline(df_)

En este método, agrupo todos los mensajes por fecha y hora. Las marcas de tiempo que creé anteriormente tienen el formato “HH:MM”. El número de mensajes por minuto no es una métrica conveniente, así que dividí todos los valores por 60 para obtener el número de mensajes por segundo.

El resultado se ve así:

Todos los mensajes de Twitter, Imagen de autor

El código se había estado ejecutando durante aproximadamente 10 días en Raspberry Pi. Como resultado, se recopilaron 6,487,433 mensajes de Twitter realizados por 1,515,139 usuarios únicos. Pero en la imagen podemos ver algunos problemas. Faltan algunos intervalos; probablemente no había acceso a Internet en ese momento. Otro día falta parcialmente, y no sé qué causó este problema; probablemente una cuenta de Twitter gratuita tiene la prioridad más baja en comparación con todas las demás solicitudes. De todos modos, no podemos quejarnos del API gratuito, y mi objetivo era recopilar los datos al menos durante una semana, y tengo suficiente información para eso. Puedo simplemente eliminar los intervalos corruptos al final:

df = df[(df['date'] >= datetime.date(2023, 5, 30)) & \        (df['date'] <= datetime.date(2023, 6, 5))]

Por cierto, otro punto en la línea de tiempo llamó mi atención; el pico ocurrió el 4 de junio cuando el número de mensajes por segundo se duplicó literalmente. Me dio curiosidad saber qué era. Podemos filtrar fácilmente el dataframe:

df_short = df[(df['datetime_hhmm'] >= datetime.datetime(2023, 6, 4, 23, 35, 0)) & \              (df['datetime_hhmm'] <= datetime.datetime(2023, 6, 4, 23, 55, 0))]with pd.option_context('display.max_colwidth', 80):    display(df_short[["created_at", "full_text"]])

El resultado se ve así:

Tweets publicados durante el intervalo de pico, Imagen del autor

Resultó que el popular jugador de fútbol Zlatan Ibrahimovic del AC Milan anunció su retiro a los 41 años, y este mensaje causó muchos reposteos en Twitter:

Línea de tiempo de mensajes de Twitter, Imagen del autor

Como podemos ver, la duración del pico fue de aproximadamente una hora; tal vez podría haber sido más largo, pero era tarde; según la línea de tiempo, el anuncio se hizo a las 23:35.

Pero volvamos a Pandas. Para un análisis de tiempo adicional, creemos dos métodos auxiliares para dibujar todos los mensajes, agrupados por la hora del día:

from bokeh.io import showfrom bokeh.plotting import figure, output_filefrom bokeh.models import ColumnDataSourcefrom bokeh.transform import linear_cmapfrom bokeh.palettes import *def draw_dataframe(p: figure, df_in: pd.DataFrame, color: str, legend_label: str):    """ Dibuja todos los mensajes en la línea de tiempo de 00..24 """    messages_per_day = df_in.groupby(['time'], as_index=False).size()        days_total = df["date"].unique().shape[0]    msg_time = messages_per_day['time']    # Los datos se resumieron por minuto, div por 60 para obtener segundos    msg_count = messages_per_day['size']/(days_total*60)      source = ColumnDataSource(data=dict(xs=msg_time, ys=msg_count))        p.vbar(x='xs', top='ys', width=datetime.timedelta(seconds=50),            color=color, legend_label=legend_label, source=source)                def draw_timeline(df_filtered: pd.DataFrame, df_full: pd.DataFrame):    """ Dibuja la línea de tiempo como un gráfico de barras """    p = figure(width=1600, height=400, title="Mensajes por segundo", x_axis_type="datetime", x_axis_label='Tiempo')        palette = RdYlBu11    draw_dataframe(p, df_full, color=palette[0], legend_label="Todos los valores")    if df_filtered is not None:        draw_dataframe(p, df_filtered, color=palette[1], legend_label="Valores filtrados")            p.xgrid.grid_line_color = None    p.x_range.start = 0    p.x_range.end = datetime.time(23, 59, 59)    p.xaxis.ticker.desired_num_ticks = 24    p.toolbar_location = None    show(p)

Esto nos permitirá ver todos los mensajes en una única línea de tiempo de 24 horas:

draw_timeline(df_filtered=None, df_full=df)

El parámetro opcional “df_filtered” se usará más adelante. El resultado se ve así:

Mensajes por día, imagen del autor

Podemos ver claramente una diferencia de día y noche, por lo que mi suposición de que la mayoría de los mensajes en holandés se hicieron desde la misma zona horaria fue correcta.

También podemos dibujar una línea de tiempo para un solo usuario. Ya utilicé este método en la parte anterior. Para la comodidad de los lectores que puedan usar este artículo como tutorial, también colocaré el código aquí:

def draw_user_timeline(df_in: pd.DataFrame, user_name: str):    """ Dibuja la hora acumulada de los mensajes para un usuario específico """    df_u = df_in[df_in["user_name"] == user_name]    # Agrupar mensajes por hora del día    mensajes_por_día = df_u.groupby(['time'], as_index=False).size()    tiempo_mensaje = mensajes_por_día['time']    cantidad_mensaje = mensajes_por_día['size']          # Dibujar    p = figure(x_axis_type='datetime', width=1600, height=150,                title=f"Línea de tiempo acumulativa de tweets: {name} ({sum(cantidad_mensaje)} mensajes)")    p.vbar(x=tiempo_mensaje, top=cantidad_mensaje, width=datetime.timedelta(seconds=30), line_color='black')    p.xaxis[0].ticker.desired_num_ticks = 30    p.xgrid.grid_line_color = None    p.toolbar_location = None    p.x_range.start = datetime.time(0,0,0)    p.x_range.end = datetime.time(23,59,0)    p.y_range.start = 0    p.y_range.end = 1    p.yaxis.major_tick_line_color = None    p.yaxis.minor_tick_line_color = None    p.yaxis.major_label_text_color = None    show(p)draw_user_timeline(df, user_name="Ell_____")

El resultado se ve así:

Línea de tiempo de mensajes para un solo usuario, imagen del autor

4. Transformación de datos

En el paso anterior, obtuvimos un dataframe “sin procesar” de los mensajes hechos por todos los usuarios. Vamos a buscar patrones diarios, así que como datos de entrada, obtengamos el número de mensajes agrupados por hora y calculados para cada usuario:

mensajes_por_usuario = df.groupby(['user_name', 'hour'], as_index=True).size()display(mensajes_por_usuario)

El resultado se ve así:

Como recordatorio, estaba usando datos de 7 días. En este ejemplo, podemos ver que durante este intervalo, el usuario publicó 4 mensajes a las 7 a.m., 1 mensaje a las 8 a.m., 3 mensajes a las 5 p.m., y así sucesivamente.

Para el análisis, decidí usar tres métricas:

  • El número total de “horas ocupadas” por día cuando el usuario estaba haciendo publicaciones en Twitter (en el último ejemplo, el número es 5).
  • El número total de mensajes por usuario (en el último ejemplo, el número es 20).
  • Un array de 24 números, que representa el número de mensajes agrupados por hora. Como paso importante, también normalizaré la suma del array al 100%.

La salida será un nuevo dataframe, agrupado por nombres de usuario. Este método realiza todos los cálculos:

def get_user_hours_dataframe(df_in: pd.DataFrame):       """ Obtener nuevo dataframe de usuarios """        horas_ocupadas = []    mensajes = []    vectores_hora = []    vectores_por_hora = [[] for _ in range(24)]    mensajes_por_usuario = df_in.groupby(['user_name', 'hour'], as_index=True).size()    usuarios = mensajes_por_usuario.index.get_level_values('user_name').unique().values    for ind, user in enumerate(users):        if ind % 50000 == 0:            print(f"Procesando {ind} de {users.shape[0]}")        horas_todas = [0]*24        for hr, value in mensajes_por_usuario[user].items():            horas_todas[hr] = value                    horas_ocupadas.append(get_busy_hours(horas_todas))        mensajes.append(sum(horas_todas))        vectores_hora.append(np.array(horas_todas))        horas_normalizadas = get_hours_normalized(horas_todas)        for hr in range(24):            vectores_por_hora[hr].append(horas_normalizadas[hr])          print("Creando el dataframe...")    cdf = pd.DataFrame({        "user_name": usuarios,        "mensajes": mensajes,        "horas": vectores_hora,        "horas_ocupadas": horas_ocupadas    })    # Añadir columnas de hora al dataframe    for hr in range(24):        cdf[str(hr)] = vectores_por_hora[hr]    return cdf.sort_values(by=['mensajes'], ascending=False) def get_hours_normalized(horas_todas: List) -> np.array:    """ Normalizar todos los valores en la lista al 100% de la suma total"""    a = np.array(horas_todas)    return (100*a/linalg.norm(a, ord=1)).astype(int)df_usuarios = get_user_hours_dataframe(df)with pd.option_context('display.max_colwidth', None):    display(df_usuarios)

El resultado se ve así:

Métricas de datos, agrupadas por usuario, imagen del autor

Ahora tenemos un dataframe con todas las métricas, y estamos listos para divertirnos con estos datos.

5. Análisis

En el último paso, transformamos un dataframe “crudo” con todos los mensajes de Twitter en los datos, agrupados por usuario. Este dataframe es realmente mucho más útil. Como calentamiento, comencemos con algo simple. Obtengamos el número de mensajes por usuario. El dataframe ya está ordenado, y podemos ver fácilmente los “Top 5” de usuarios que publicaron el máximo número de mensajes:

display(df_users[:5])
Dataframe de métricas, agrupado por usuario, imagen del autor

También encontremos percentiles:

> print(df_users["messages"].quantile([0.05, 0.1, 0.5, 0.9, 0.95]))0.05     1.00.10     1.00.50     1.00.90     4.00.95    10.0

El resultado es interesante. Estos datos se recopilaron en 7 días. Hay 1,198,067 usuarios únicos en el dataframe que publicaron al menos un mensaje durante este período. Y el percentil 90 es solo 4, lo que significa que el 90% de todos los usuarios publicaron solo 4 mensajes durante esta semana. Una gran diferencia, en comparación con los principales usuarios, que publicaron más de 5000 tweets. Pero como se discutió en la primera parte, algunos de los “principales usuarios” probablemente sean bots. Bueno, podemos verificar fácilmente esto usando el número de mensajes por hora. Ya tengo un número de mensajes, agrupados por hora y normalizados al 100%. Encontremos usuarios que estén publicando mensajes continuamente, sin retrasos. Para hacer esto, solo necesitamos filtrar usuarios que estuvieron publicando el 100/24 = 4% de sus mensajes cada hora:

    df_users_filtered = df_users.copy()    for p in range(24):        df_users_filtered = df_users_filtered[(df_users_filtered[str(p)] >= 2) & \                                              (df_users_filtered[str(p)] <= 5)]            display(df_users_filtered)        for user_name in df_users_filtered["user_name"].values:        draw_user_timeline(df, user_name)

El número puede no ser exactamente del 4%, por lo que utilicé el rango de filtro de 2 a 5%. Como resultado, se encontraron 28 “usuarios” que estaban publicando el mismo número de mensajes cada hora:

“Usuarios”, publicando mensajes con intervalos iguales, imagen del autor

En la parte anterior, ya había detectado algunos bots utilizando el algoritmo de agrupamiento. Aquí podemos ver que incluso con un enfoque mucho más simple, podemos obtener resultados similares.

Vamos a una parte más divertida y agrupemos usuarios por su tiempo de actividad. Debido a que la cantidad total de mensajes por hora se normaliza al 100%, es posible hacer solicitudes bastante complejas. Por ejemplo, agreguemos nuevas columnas “mañana”, “día”, “tarde” y “noche”:

df_users["noche"] = df_users["23"] + df_users["0"] + df_users["1"] + df_users["2"] + df_users["3"] + df_users["4"] + df_users["5"] + df_users["6"]df_users["mañana"] = df_users["7"] + df_users["8"] + df_users["9"] + df_users["10"]df_users["día"] = df_users["11"] + df_users["12"] + df_users["13"] + df_users["14"] + df_users["15"] + df_users["16"] + df_users["17"] + df_users["18"]df_users["tarde"] = df_users["19"] + df_users["20"] + df_users["21"] + df_users["22"]

Para el análisis, solo usaré aquellos usuarios que hayan publicado más de 10 mensajes:

df_users_ = df_users[(df_users['messages'] > 10)]df_ = df[df["user_name"].isin(df_users_["user_name"])]

Por supuesto, 10 no es un número estadísticamente significativo. Esto es solo una prueba de concepto y para una investigación real, se recomienda recolectar datos en intervalos más largos.

De todas formas, los resultados son interesantes. Por ejemplo, podemos encontrar usuarios que publicaron la mayoría de sus mensajes en la mañana, usando solo una línea de código. También podemos obtener todos estos mensajes y dibujarlos en la línea de tiempo:

df_users_filtered = df_users_[df_users_['morning'] >= 50]print(f"Usuarios: {100*df_users_filtered.shape[0]/df_users_.shape[0]}%")df_filtered = df_[df_["user_name"].isin(df_users_filtered["user_name"])]draw_timeline(df_filtered, df_)

El resultado se ve así:

Usuarios publicando tweets por la mañana, imagen del autor

Curiosamente, este número es solo alrededor del 3%. Como comparación, el 46% de los usuarios activos envían más del 50% de sus mensajes durante el día:

Usuarios publicando tweets durante el día, imagen del autor

Podemos hacer otras solicitudes; por ejemplo, encontremos usuarios que están haciendo el 80% de sus mensajes por la noche:

df_users_filtered = df_users_[df_users_['evening'] >= 80]

El resultado se ve así:

Usuarios publicando tweets por la noche, imagen del autor

También podemos mostrar la línea de tiempo de algunos usuarios para verificar los resultados:

for user_name in df_users_filtered[:5]["user_name"].values:    draw_user_timeline(df_, user_name)

La salida se ve así:

Línea de tiempo de usuarios seleccionados, imagen del autor

Los resultados pueden ser interesantes; por ejemplo, el usuario “rhod***” estaba publicando casi todos los mensajes a la misma hora después de las 19:00.

Nuevamente, debo repetir que estos resultados no son definitivos. Analicé solo usuarios más o menos activos que publicaron más de 10 tweets en una semana. Pero un número significativo de usuarios publicó menos mensajes y para obtener más información sobre ellos, se deben recolectar datos durante varias semanas o incluso meses.

Conclusión

En este artículo, pudimos obtener todos los mensajes de Twitter publicados en un idioma específico, en nuestro ejemplo, holandés. Este idioma se usa principalmente en los Países Bajos y Bélgica, que están ubicados cerca uno del otro. Esto nos permite conocer la zona horaria del usuario, que, lamentablemente, no pude obtener de la API de Twitter, al menos usando la cuenta gratuita. Al analizar las marcas de tiempo de los mensajes, podemos obtener mucha información interesante; por ejemplo, es posible saber si más usuarios están activos por la mañana, durante las horas de trabajo o por la noche. Encontrar patrones temporales en el comportamiento de los usuarios puede ser útil para la psicología, la antropología cultural o incluso la medicina. Millones de personas están usando las redes sociales y es interesante saber cómo afecta nuestras vidas, el ritmo de trabajo o el sueño. Y como se mostró en el artículo, analizar este comportamiento se puede hacer usando solicitudes simples, que en realidad no son más difíciles que las matemáticas escolares.

También fue interesante ver cuántos datos pueden almacenar las redes sociales. Supongo que la mayoría de las personas nunca piensan en cuántos mensajes se publican. Incluso para una comunidad de habla holandesa relativamente pequeña (alrededor de 25 millones de hablantes nativos en el mundo), se pueden generar más de 10 tweets por segundo. Para este artículo, se analizaron 6,487,433 mensajes de Twitter de 1,515,139 usuarios, ¡y estos fueron solo mensajes publicados en 10 días! Para países más grandes como Alemania, obtener todos los mensajes probablemente estará más allá de las limitaciones de una cuenta de desarrollo gratuita de Twitter. En ese caso, puede ser posible combinar diferentes consultas de solicitud con filtrado por ubicación de usuario.

En cualquier caso, las redes sociales son una fuente interesante de información sobre nosotros, y deseo a los lectores buena suerte en sus propios experimentos. Aquellos interesados también son bienvenidos a leer la primera parte sobre el uso del algoritmo K-Means para agrupar usuarios de Twitter. Y como otro enfoque, también se explicó el análisis NLP de publicaciones de Twitter.

Si disfrutaste esta historia, siéntete libre de suscribirte a Zepes, y recibirás notificaciones cuando se publiquen mis nuevos artículos, así como acceso completo a miles de historias de otros autores.

Gracias por leer.