Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Propuesta de lección: Uso de las colecciones del HathiTrust para el análisis de textos literarios en R #579

Open
jenniferisasi opened this issue Jul 14, 2023 · 32 comments

Comments

@jenniferisasi
Copy link
Contributor

jenniferisasi commented Jul 14, 2023

The Programming historian en español ha recibido una propuesta de lección con el título provisional "Uso de las colecciones del HathiTrust para el análisis de textos literarios en R" por @jose-eduardo.
Los objetivos de la lección son:

  1. Cómo accesar los datos (y metadatos) disponibles en el sitio de HathiTrust a través de su API.
  2. Cómo utilizar R para transformar y manipular los datos obtenidos de manera que sea posible usarlos para la minería textual,
  3. Cómo crear las “colecciones” de metadatos en HathiTrust y su utilidad (y limitaciones) para avanzar la investigación en español.

La fecha aproximada para la entrega de la lección es el 31 de agosto de 2023. Si no se entrega para entonces, la editora manager contactará con el autor de la lección para sugerir otra fecha. Si no recibe noticias, el ticket se cerrará. Éste podrá abrirse en el futuro a petición del autor.

El principal contacto para esta lección es, por ahora, @jenniferisasi. Si se produce algún problema, el autor puede contactar con nuestra ’ombudsperson' (Silvia Gutiérrez de la Torre - http://programminghistorian.org/es/equipo-de-proyecto).

@jenniferisasi
Copy link
Contributor Author

Acuso recibo del borrador de la lección de parte de @jose-eduardo. Como ME voy a darle una primera vuelta para asegurarme de que el markdown y demás son correctos para que funcione la previsualización.

En los próximos días pasaré los archivos a @anisa-hawes y estarán listos para que @JoshuaGOB dé inicio a la edición y revisión de la lección.

@jenniferisasi
Copy link
Contributor Author

Notas para que @JoshuaGOB tenga en cuenta:

  • Al instalar los primeros paquetes, me pide que elija unas opciones (he puesto el 1 en mi caso y, de momento, todo ha funcionado):
    `> remotes::install_github("xmarquez/hathiTools")
    Downloading GitHub repo xmarquez/hathiTools@HEAD
    These packages have more recent versions available.
    It is recommended to update all of them.
    Which would you like to update?
1: All                            
2: CRAN packages only             
3: None                           
4: openssl (2.0.6 -> 2.1.0) [CRAN]
5: cpp11   (0.4.4 -> 0.4.6) [CRAN]
6: purrr   (1.0.1 -> 1.0.2) [CRAN]
7: fs      (1.6.2 -> 1.6.3) [CRAN]

@anisa-hawes
Copy link
Contributor

@jenniferisasi
Copy link
Contributor Author

¡Gracias @anisa-hawes! Con esto, queda en manos de @JoshuaGOB para que inicie el proceso de edición y después de revisión por pares.

@jose-eduardo
Copy link
Collaborator

Ahora me doy cuenta de tantos errores. La "table" salió mal-- mi inexperiencia con Markdown es el culpable en este caso. Gracias!

@anisa-hawes
Copy link
Contributor

Apologies, @jose-eduardo. I missed that, but I have fixed it now!

--

Mis disculpas, @jose-eduardo. Se me pasó, ¡pero ya lo he arreglado!

@rivaquiroga
Copy link
Member

@jenniferisasi, si necesitas revisores para esta lección yo estoy disponible 👀

@jose-eduardo
Copy link
Collaborator

@anisa-hawes the R file "obtener_tokens.r" (for those using Windows) should be included in "assets" --I guess I forgot to include a link to that in the tutorial (sorry)

@anisa-hawes
Copy link
Contributor

Dear @jose-eduardo. Apologies – I wrote myself a note to ask Jenn about this.

I've uploaded the file 878562a and also added a link from line 457 73286d5. (Is this the right place for the link?)

@jenniferisasi
Copy link
Contributor Author

Thank you @anisa-hawes! Yes, that is the correct placement on the link :)

@JoshuaGOB
Copy link
Collaborator

JoshuaGOB commented Aug 22, 2023

Saludos @jose-eduardo,

Quiero felicitarte por esta excelente lección. Es un aportación importantísima para el estudio de literatura a gran escala. Ya terminé la primera pasada y la semana que viene debo tener las sugerencias finales. Aquí apunto los detallitos que encontré en esta versión.

¡Gracias por su ayuda, @jenniferisasi y @anisa-hawes !

Estoy utilizando los números de párrafos que se encuentran en la previsualización de la lección

  • 3
  • las escasez - las escasez
  • Hathitrust, solo una pequeña - eliminar coma
  • 4
  • de Trayn Dewar, y - eliminar coma
  • de Nabeel Siddiqui. - de Nabeel Siddiqui, ambas traducidas al español por Jennifer Isasi.
  • 5
  • descargarlo e instalarlo - descargarlo e instalarlo en Windows y en este otro video para hacer lo mismo en una Mac.
  • 6
  • añadir enlace a los paquetes la primera vez que se mencionan:
    paquete de R llamado hathiTools
    primero el paquete remotes
  • 7
  • Añadir corchetes para marcar el código y enlaces para mantener consistencia con otras lecciones de R
> install.packages("remotes")
> remotes::install_github("xmarquez/hathiTools")
> library(hathiTools)

tidyverse, readr, readxl y stringr y, por último, necesitas tener instalado pero no cargado el paquete de plyr.

ggplot2, tmap, rnaturalearth, y sf.

#para manipular datos y archivos
>library(tidyverse)
>library(readr)
>library(readxl)
>library(stringr)

#para mapas y visualización
>library(rnaturalearth)
>library(ggplot2)
>library(tmap)
>library(sf)
  • 25
  • añadir explicación de la fecha: los que no tienes acceso completo por aún tener restricciones de derechos de autor y necesites saber el contexto en que una palabra está siendo utilizada
  • 28
  • seleccionar esa opción y continuar - añadir un punto al final de la oración
  • 38
  • terminado de incluir todo lo quieras - terminado de incluir todo lo que quieras
  • 45
  • El proceso transformación de tus datos - El proceso de transformación de tus datos
  • 77
  • dos mapas casi no hay casi representación - dos mapas casi no hay representación

@jose-eduardo
Copy link
Collaborator

Gracias por las correcciones @JoshuaGOB . Solo una pregunta, después que terminen con las correcciones, ¿cómo se procede?--¿yo hago las correcciones y les envío una copia por email?
Gracias a todos

@anisa-hawes
Copy link
Contributor

anisa-hawes commented Aug 23, 2023

Hola @jose-eduardo,

We invite you to make the adjustments to the file, which is here: /es/borradores/originales/uso-las-colecciones-hathitrust-mineria-textual-R.md.

We don't use the Pull Request system during this Phase of the workflow, rather authors can make direct changes to their lesson. GitHub's agility for capturing successive versions of a file is a great asset to us in this Phase: Joshua will be able to review exactly what has been edited in the file's commit history.

Please ask me if you have any questions or uncertainties – I'm more than happy to help.

@jose-eduardo
Copy link
Collaborator

Made all the necessary changes. Not sure if there is anything else I need to do here.

@anisa-hawes
Copy link
Contributor

Hola @jose-eduardo,

Thank you for your edits in response to Joshua's feedback. The next step is for @JoshuaGOB to review the revised version, then identify two peer reviewers who we will invite to respond to your lesson.

Very best wishes,
Anisa

@JoshuaGOB
Copy link
Collaborator

JoshuaGOB commented Sep 24, 2023

Hola @jose-eduardo
Disculpa la demora y gracias por tu paciencia.

Después de incorporar estas últimas sugerencias, la lección estaría lista para las revisoras.

  • Utilizar tmap v4 o añadir una nota sobre el cambio ya que se recibe este mensaje: Breaking News: tmap 3.x is retiring. Please test v4, e.g. with
    remotes::install_github('r-tmap/tmap')

Desde la línea 338 a 370, considera añadir comandos para ilustrar los datos antes y después de los cambios:

  • 359 - "Repetir el comando anterior para ver el cambio en estas filas:
    metadatos %>% filter(str_detect(author, "Icaza")) %>% select(author, title)"

  • 360 - ...del nombre. Para representarlos antes de hacer los cambios:

>filtro_regex <- metadatos[c(3, 36, 77, 94), "author"]
>print(filtro_regex)
*también esta alternativa*
>print(paste("Antes de modificar:", filtro_regex))
  • 360 - Puedes modificar los primeros tres y confirma el cambio con las siguientes expresiones:
>metadatos$author[c(3,36,94)]<-sub("\\...*", "", metadatos$author[c(3,36,94)])
>filtro_regex <- metadatos[c(3, 36, 77, 94), "author"]
>print(filtro_regex)
"considerar también"
>print(paste("Después de modificar:", filtro_regex))
  • 369 - La misma sugerencia a la anterior:
>metadatos$author[77]<-sub("\\s+[^ ]+$", "", metadatos$author[77])
>filtro_regex <- metadatos[c(3, 36, 77, 94), "author"]
>print(filtro_regex)

"considerar también:"
>print(paste("Segunda modificación:", filtro_regex))
  • 557 - Parece que hay un corchete demás después de la barra vertical:
>ciudades_encontradas<-ciudades_encontradas |> group_by(GRUPO, prov) | > mutate(num_por_prov = sum(ocurrencias_por_50_mil))

Tan pronto estés listo, presentamos a las revisoras. Es una excelente lección y me encantó aprender tanto de R como de HathiTrust.

@jose-eduardo
Copy link
Collaborator

Ya está todo. He añadido además unos consejos en la última sección para los usuarios que estén trabajando con cientos y cientos de datos y el Rstudio no tenga la capacidad para manejar eso.

@jenniferisasi
Copy link
Contributor Author

¡Súper, @jose-eduardo! Gracias por hacer los cambios sugeridos por @JoshuaGOB.

@JoshuaGOB, me dices si te puedo echar una mano en algo - solo estoy pasando por todos los tickets para estar al día.

@JoshuaGOB
Copy link
Collaborator

Saludos, @jose-eduardo Revisé los cambios y los consejos en la última sección me parecen perfectos.

La revisión de la lección está en las excelentes manos de @jenniferisasi y @rivaquiroga. Tan pronto tengamos sus sugerencias, la lección puede pasar al próximo paso.

@rivaquiroga
Copy link
Member

¡Hola, @JoshuaGOB! Para la revisión seguimos ese orden? Es decir, primero @jenniferisasi hace su revisión y luego yo?

@JoshuaGOB
Copy link
Collaborator

JoshuaGOB commented Nov 16, 2023 via email

@rivaquiroga
Copy link
Member

¡Hola! Podría hacer mi revisión después del 12 de diciembre (tengo que entregar mi tesis ese día 😬)

@jenniferisasi
Copy link
Contributor Author

¡Hola! Vamos a ver si yo puedo hacer mi revisión antes de esa fecha entonces. @rivaquiroga, nos tendrás que contar de tu tesis :)

@jenniferisasi
Copy link
Contributor Author

jenniferisasi commented Dec 12, 2023

Estimados. Doy comienzo a mi revisión. Trataré de hacerlo de una pero si ven que faltan cosas, seguiré con ello pronto:

  • 8: Con la nueva versión de Hathitrust (supongo), la URL cambió a babel.hathitrust.org y http://hdl.handle.net/2027/uc1.31175010656638 redirecciona a https://babel.hathitrust.org/cgi/pt?id=uc1.31175010656638&seq=9; ahora el ID del libro es lo que está entre el = y &
  • 8: Creo que valdría editar un poco la explicación para aclarar que se necesita abrir la full view del libro para conseguir el ID. Me he dado cuenta tratando de buscar el ID de un libro que quiero probar.
  • 8: Problema: no me funciona con todos los textos que pruebo
  • 15: Para quienes no sepan tanto de R, sería adecuado añadir que hay que saber la palabra exacta que se utiliza para dividir capítulos o secciones, o no funcionaría - se indica en el párrafo 20 más o menos, pero vale la pena ponerlo ya aquí.
  • 18: " Ya sabemos que la novela comienza en la página 17" --> "En este caso sabemos que la novela comienza..."
  • 22: Valdría la pena decir que el número de página de la tibble no tiene que coincidir con el número de pagina impreso en los libros; pues se trata de la página de escaneado, ¿verdad? Me ha costado un poco comprobarlo.
  • 25: Sería extra: ¿Cómo veo si este grupo de palabras es relativamente importante respecto del resto de tokens de la novela? Es decir, ¿puedo calcular si es un tema clave?

Hago pausa en la sección de analizar colecciones pero sigo pronto.

@jose-eduardo
Copy link
Collaborator

No sé si debo explicar ahora o esperar a que Jennifer termine su revisión (?).

Puedo aclarar por el momento que http://hdl.handle.net/2027/ es la dirección oficial para buscar los libros por ID. Sí, te redirige a "babel... etc...", pero si en el futuro Hathitrust decidiera cambiar de "babel" a otro sitio, te redirigiría al nuevo. (Véase para esto la lección https://programminghistorian.org/en/lessons/text-mining-with-extracted-features)

@jenniferisasi
Copy link
Contributor Author

Hola @jose-eduardo. Disculpa la demora en responder - me desconecté lo más posible del trabajo durante las vacaciones.

Puedes esperar a que demos todas nuestras revisiones antes de hacer ningún cambio, para evitar darle demasiadas vueltas al texto/archivo. Como seguí las instrucciones de la lección para nuestro experimento en MLA, espero terminar de revisar y dejar mis comentarios aquí mañana mismo, viernes 12 de enero.

@jenniferisasi
Copy link
Contributor Author

Listo. Perdón por el nuevo retraso.

Verifiqué dos veces que del párrafo 25 en adelante todo funciona y sí. Puede que @rivaquiroga, que enseña R de forma regular, tenga sugerencias extra.

Probé el código como uno de los 3 métodos que @JoshuaGOB y yo probamos para MLA. Este resultó el menos "limpio", por así decirlo, porque - cómo bien apuntas en las notas finales - al buscar lugares por token, se pierde el contexto y no se puede verificar si el lugar está en, no sé, México o Venezuela. Tampoco hicimos el mapa porque las novelas eran de muchos países, pero aquí funciona de maravilla por ser el tema estudiado la novela de Ecuador.

@rivaquiroga y @JoshuaGOB queda en vuestras manos :)

@rivaquiroga
Copy link
Member

¡Hola! A fines de la próxima semana envío mi revisión 🤓

@rivaquiroga
Copy link
Member

rivaquiroga commented Feb 20, 2024

¡Hola, @jose-eduardo!

Muchas gracias por tu lección. Conocía hathiTrust, pero nunca había explorado con detalle como ahora sus funcionalidades. Quedé muy entusiasmada y con muchas ideas ✨. La lección permite hacerse una idea general de cómo funciona y de su potencial para hacer análisis exploratorios.

A continuación hay algunas sugerencias que se me han ocurrido luego de leer la lección y realizar los ejercicios propuestos. Primero hay un comentario general sobre el formato del código y luego comentarios más detallados por sección o párrafo. Como solo los párrafos de texto tienen número, a los de código hice referencia indicando el número del párrafo anterior.

Sugerencias generales

  • Sería importante eliminar el > antes de cada línea de código. Al copiar y pegar el código de la lección en mi sesión de R muchas veces se me pasaron esos signos porque están pegados al primer elemento del bloque de código. El estilo código en markdown es suficiente para saber que no es texto.
  • Es necesario formatear el código para que no quede en una sola línea, sobre todo en aquellos casos en que se utiliza un pipe (%>%). Como el ancho del párrafo tiene un límite, hay que mover la barra para poder ver el código completo, como acá:
codigo-ancho

Si tienes todo el código en un script, la manera más rápida de resolver eso es con el paquete styler, que puede formatear todo el código de un archivo. No solo va a agregar los saltos de línea necesarios después de cada pipe, sino que también va a agregar espacios entre elementos del código (por ejemplo, antes y después de <-).

Sugerencias por sección/párrafo

Objetivos

  • Al final de este apartado se podría anunciar la estructura de le lección para que sea más fácil seguir el hilo conductor. Es decir, agregar una carta de navegación. Los objetivos dejan claro qué se va a aprender, pero sin la carta de navegación me costó un poco al principio entender hacia dónde iba la lección. Se ve que hay dos grandes partes, una que muestra cómo explorar un volumen y otra que muestra cómo explorar una colección y visualizarla a través de un mapa. Saber eso de antemano ayudaría a quien lee la lección a seguirla mejor.

Requisitos

  • Quizás sería bueno explicitar cuáles son las nociones básicas de R que se necesita manejar. Por ejemplo, señalar que a lo largo de la lección se utilizará código para instalar y cargar paquetes, importar datos en formato excel y csv, manipular datos con la sintaxis del tidyverse y crear un mapa. Si uno sabe leer el código que hace eso, debería poder seguir la lección sin problema.
  • Es necesario explicitar que la lección asume familiaridad con el uso de expresiones regulares.

Instalar y cargar paquetes

  • Pensando en la sostenibilidad de la lección a largo plazo, sería preferible no utilizar el paquete plyr, porque es un paquete que está retirado. La recomendación de su autor es utilizar dplyr o purrr, dependiendo de la tarea que se quiera realizar (https://github.com/hadley/plyr). Vuelvo sobre este punto en la sección de más abajo en la que se usa el script adicional.
  • ¶ 6. - bloque de código Sería práctico señalar explícitamente cuáles son todos los paquetes que es necesario instalar (tidyverse, rnaturalearth, tmap y sf). Es decir, proveer la línea de código tal como se hace para hathiTools.
  • ¶ 6. - bloque de código Quizás se podría avisar que sale un mensaje informativo largo cuando uno carga hathiTools. A veces las personas con menos experiencia piensan que esos mensajes son un error.
  • ¶ 7 - bloque de código. Sugiero eliminar la línea de código library(tidyverse). Esa línea carga dplyr, ggplot2, tidyr, readr, purrr, tibble, stringr, forcats y lubridate. Para algunos de esos paquetes se agrega luego una línea de código para cargarlos de forma explícita. El único que no aparece y que habría que agregar luego de eliminar la llamada al tidyverse es dplyr.
  • ¶ 7 - bloque de código. @JoshuaGOB ya mencionó lo del mensaje que arroja tmap. Al respecto, me parece que habría dos opciones. La primera, sería avisar que sale este mensaje y dejar por ahora el código como está porque la versión disponible en CRAN es la 3.x y la 4.x todavía está solo disponible en GitHub. No sé si hay una fecha clara para el release, pero por ahora la versión 3 es la que sigue vigente. Esto implicaría que cuando la versión 4 llegue a CRAN habría que actualizar el código para que siga funcionando. La opción 2 sería instalar la versión de GitHub y si es necesario actualizar el código de la lección. Esto implicaría que cuando la versión 4 llegue a CRAN habría que cambiar el código de instalación para usar install.packages(). Quizás @jenniferisasi como Managing Editor tenga una opinión respecto de cuál de las dos opciones sería mejor en términos de la sostenibilidad de la lección.
  • Siguiendo con tmap, sería importante avisar que su instalación en Linux o macOS podría requirir pasos adicionales. Se puede incluir un enlace a la sección del README en donde se proveen links con las indicaciones para cada sistema operativo.

Obtener los atributos extraídos de un volumen

  • ¶ 10. Sugiero utilizar "dataframe" en vez de "tibble". Un tibble es un data frame, solo que tiene una manera particular de imprimirse en la consola de R. Pero la estructura de datos propiamente tal es un data frame.
  • ¶ 11. Sugiero eliminar "Antes de explorar los detalles de los resultados recibidos, hagamos un pequeño ejercicio." El ejercicio es parte de la exploración que se está haciendo del volumen. No se percibe como algo aparte.
  • ¶ 11. Sugiero eliminar "con solo una línea de código" al final del párrafo. Técnicamente es más de una, solo que como no se pusieron saltos de línea después de cada pipe, quedan como una sola.
  • ¶ 12-17. Al principio de la lección se indica que se va a usar ggplot para hacer gráficos. ¿Quizás por consistencia sea mejor usar ese paquete para esos gráficos? Por ejemplo:
ggplot(tokens_maria, aes(page, num_tokens)) +
  geom_line(color = "blue") +
  labs(x = "páginas", y = "tamaño") +
  theme_classic()

Y luego con la línes roja para los capítulos:

ggplot(tokens_maria, aes(page, num_tokens)) +
  geom_line(color = "blue") +
  geom_vline(xintercept = capitulos$page, color = "red") +
  labs(x = "páginas", y = "tamaño") +
  theme_classic()
  • ¶ 15 - bloque de código. En filter(token %in% "CAPÍTULO") sería mejor cambiar el operador %in% por == porque solo se está utilizando un valor para hacer el filtro.
  • ¶ 18. Sería útil para quien sigue la lección que se explicitaran más cómo se llegó a las conclusiones que se presentan en este párrafo. Por ejemplo, explicar que antes de la primera línea roja en el gráfico (es decir, el primer capítulo) se ve que hay una sección relativamente grande que contiene texto que no estaría dentro de los capítulos de la novela. Y que si uno revisa con detalle el objeto maria, podría ver que es porque hay un prólogo. Respecto de que la novela propiamente tal empieza en la página 17, sería bueno explicitar que eso se puede saber al explorar el objeto capitulos.
  • ¶ 18 - bloque de código. Reemplazar %in% por ==.
  • ¶ 19 - bloque de código. Hay que hacer una pequeña edición en el código que filtra las páginas. Tal como está, es decir, filter(page > 17 & page < 443), no incluye el contenido de las páginas 17 y 443. Y en esas páginas sí hay contenido que es parte de la novela. Tendría que ser filter(page >= 17 & page <=443)para incluir todo el contenido. Habría que ajustar el resultado del recuento siguiente para que coincida con este cambio.

Una mirada global a nuestro marco de datos

  • ¶ 18. Sugiero modificar la explicación de POS por "“POS” (Part of Speech, en inglés) corresponde a la categoría gramatical de cada token, siguiendo el esquema de anotación del proyecto Universal Dependencies (Dependencias Universales)." En general no se usa la traducción literal de "parte de habla" o "parte del discurso" porque en español esas expresiones aluden a otro nivel de la lengua, no a las categorías gramaticales que es la información que contienen las etiquetas POS. Es necesario propagar este cambio más adelante en la lección. Es decir, cada vez que se mencione "partes del discurso" o "partes del habla", usar "categorías gramaticales".
  • ¶ 22. "Al incluir el nombre de la columna puedes indicar la sección que te interesa." > "Para ello, puedes filtrar la tabla indicando la sección que te interesa. "
  • ¶ 23. Filtra todos los tokens en el cuerpo principal (“body”) que sean de caracter alfabético y elimina lo que no lo sea." > "Para ello, puedes filtrar todos los tokens del cuerpo principal (“body”) que sean caracteres de tipo alfabético".
  • ¶ 23 - bloque de código. Esta parte del código tiene una doble negación: !str_detect(token, "[^[:alpha:]]"). Sería lo mismo que str_detect(token, "[:alpha:]"). Sugiero dejar esta última versión porque es más fácil de leer.
  • ¶ 25. "necesites saber el contexto en que una palabra está siendo utilizada." > "necesites saber la función con que una palabra está siendo utilizada."
  • ¶ 25 - bloque de código. ¿Por que no se incluyó enfermas? El par enfermo - enfermos sí se incluye. Por otra parte, quizás sea mejor no incluir un verbo como enfermar, porque solo se está considerando el infinitivo y no otras formas verbales.
  • ¶ 25 - bloque de código. Se podría hacer el filtro de ADJ y ADV en una sola llamada a la función filter(). Por ejemplo:
enfermedad_maria  %>%  
   filter(!(token == "mal" & POS %in% c("ADJ", "ADV")))

Trabajar con colecciones de HathiTrust

  • ¶ 28 - captura de pantalla. Como es más probable que una persona se tenga que registrar como invitado, quizás sería mejor que el pantallazo incluyera lo que aparece al lado del registro institucional, como acá:
accesso
  • ¶ 30. Sugiero agregar una captura de pantalla mostrando el menú "The collection" desplegado para hacer más fácil la navegación para personas que no se manejan tan bien con el inglés.

Crear tu propia colección

  • Al principio me costó un poco entender cómo esta sección encajaba con el resto de la lección. Tal como está, el ejemplo seleccionado corta un poco el hilo conductor. Me refiero a que se muestra cómo crear una colección, pero esta después queda "abandonada", en el sentido de que no se muestra nada concreto que trabajar con ella (como sí se hizo antes con María y luego con la colección de novelas ecuatorianas). Eso me confundió un poco la primera vez que leí la lección. Una opción sería que en vez de trabajar con novelas de los 60 de México, se adelante el ejemplo de la sección siguiente, es decir, que se empiece a crear una colección de novelas ecuatorianas (seleccionando un par de novelas, como en el ejemplo actual). Así, esta sección sería una demostración de los primeros pasos para llegar a un producto más elaborado como el que se muestra en el apartado siguiente. Y se puede explicitar el conocimiento sobre el tema que se necesita para crear la colección.
  • ¶ 38. No me queda claro por qué sería necesario cambiarle la extensión al archivo de txt a tsv. La función read_tsv() funciona con archivos de extensión txt. En general es preferible mostrar cómo trabajar con los archivos tal como los reciben los usuarios para que sea más fácil replicar esto con otros ejemplos. Ahora, si la razón es porque así un programa como Excel o Libre Office sabe así que es una hoja de cálculo y se puede abrir por defecto con una de esas aplicaciones, entonces habría que explicitarlo así. Porque tal como está da la impresión de que es un paso necesario para poder importarlo a R y no lo es.

Geografía en la novela

  • ¶ 40. reader > readr
  • ¶ 43. Sugiero simplificar la explicación dada en la primera oración de este párrafo porque resulta un poco confusa, porque la columna en el archivo tiene otro nombre y porque no es una corrección lo que se ha hecho, si no que es una variable diferente. Una opción podría ser: Ya que para este proyecto necesitamos saber con exactitud cuándo las novelas fueron publicadas, hemos creado una tabla con el número de htid de cada novela y su fecha de publicación. Este archivo, fechas.xls, lo encuentras entre los documentos que acompañan a esta lección. El próximo paso, por lo tanto, será combinar ambas tablas
  • ¶ 43 - bloque de código. Respecto del código para unir ambas tablas, como más abajo en la lección se utiliza un join, quizás acá también podría utilizar uno para hacer el código más simple:
metadatos <- left_join(metadatos, fechas) |> 
  select(-rights_date_used)
  • ¶ 44. Es necesaria una transición entre ambos párrafos que haga más claro que lo que se está presentando es un flujo de trabajo. Es decir, incluir una oración que diga que luego de importar y consolidar la tabla con los metadatos, es importante revisar si es necesario realizar alguna transformación en los mismos. Y ahí introducir que, por ejemplo, puede haber problemas en la forma en que autores y títulos están escritos.
  • ¶ 46 - 51 - bloques de código. Como la lección asume que las personas están familiarizadas con el uso de expresiones regulares, quizás se podría presentar directamente una expresión regular que resuelva todos los problemas detectados. Por ejemplo: sub("^([^,]+,\\s[^,0-9]+)([,\\.]|\\s).*", "\\1", metadatos$author). Pienso que sería más provechoso en esta sección mostrar cómo uno se aproxima a este tipo de problemas. Acá se parte del ejemplo de Jorge Icaza y luego de resolver ese caso particular se ve que quedan un par de cosas no resueltas. Y muchas veces el procedimiento es más bien identificar primero los distintos patrones en que aparecen escritos los nombres y luego escribir una o más expresiones regulares para hacer la normalización. Así se presenta una método para aproximarse un problema y no queda solo como un ejemplo para este caso en particular.
  • ¶ 51 - bloque de código.. Esta línea de código me convirtió algunos valores a NA: iconv(metadatos$author,from="UTF-8",to="ASCII//TRANSLIT").

nas

iconv no suele dar resultados consistentes entre sistemas operativos. Sugiero utilizar la función stri_trans_general() del paquete stringi. Ese paquete se instala junto con el paquete stringr, así que puede utilizar llamando directamente la función:

metadatos$author <- stringi::stri_trans_general(str = metadatos$author, id = "Latin-ASCII")
  • ¶ 52 - bloque de código.. Sugiero simplificar el código usando la función count()para no perder el nombre de la columna
metadatos %>%
  count(author, sort = TRUE) 

Obtener los atributos extraídos para el proyecto

  • ¶ 54. Sería bueno adelantar aquí qué es lo que vamos a hacer y por qué. Es decir, anunciar vamos a extraer los atributos (en particular el largo del texto) para luego agregárselo a nuestra tabla de metadatos. De lo contrario no queda tan tan clara la finalidad de todo el procedimiento hasta mucho más adelante.
  • ¶ 55 - bloque de código. Sería bueno avisar que rsync_from_hathi(metadatos$htid, dir = tmpdir)y cache_htids(metadatos$htid, dir = tmpdir)arrojan un montón de mensajes en la consola. Se podría explicar quizás a qué corresponden esos mensajes.
  • ¶ 55 - bloque de código. También sería bueno dar un estimado de cuánto se podría demorar el proceso para esta colección en particular. Obviamente varía de máquina en máquina, pero quizás dar una aproximación (por ejemplo, X minutos en un computador de X RAM).
  • ¶ 58 - bloque de código. No sé si en este caso sea bueno reescribir el objeto novelas. El código lo que hace es generar una medida de resumen a partir de una de las variables contenidas en el objeto novelas. Así que se pierden datos al reescribirlo. Si bien en esta lección no se van a utilizar esas otras variables, quizás alguien podría querer explorarlas en otro momento. Se podría elegir un nombre que indique que lo que hay ahí son frecuencias de tokens por novelas.

Windows: Otra manera de construir el marco de datos

  • Sería bueno tratar de actualizar obtener_tokens.r para que no necesite plyr. En el script sale que no funciona con bind_rows() o rbind() por que a veces el número de columnas es distinto. Si tienes un ejemplo por ahí en que haya pasado eso, sería genial poder verlo para tratar de buscar una solución que no implique usar un paquete retirado.
  • ¶ 61 - bloque de código. Como los archivos .R son de archivos de texto plano, el código de la función también se podría leer directo desde el repositorio de GitHub. Quizás se podría agregar también como opción:
source("https://raw.githubusercontent.com/programminghistorian/ph-submissions/gh-pages/assets/uso-las-colecciones-hathitrust-mineria-textual-R/obtener_tokens.r")

Análisis y visualización de datos

  • ¶ 66 - bloque de código. En las versiones más reciente de dplyr la función case_when trae un argumento (.default) para definir qué hacer con los casos para los que no se dan condiciones. Así se evita usar TRUE que no es muy "transparente". La última línea, entonces, quedaría como .default = NA_character_
  • ¶ 66 - bloque de código. Quizás en este punto sería una buena idea convertir la variable GRUPO en una de tipo factor, porque cuando se hace la tabla de grupos más adelante en ¶ 71, estos quedan en orden alfabético, no en el orden de la categoría creada. Me refiero a esa tabla:
>table(ciud_unicas$GRUPO)

 1900-25  1925-50 Pre-1900 
      71      133       54 

En ella Pre-1900 queda después de 1900-25. Si bien en la lección esa es la única parte en que afecta, si alguien quisiera hacer algún gráfico que muestre comparaciones entre esos grupos le aparecerían en desorden también si es que la variable no se convierte a factor.
La conversión se podría hacer dentro de case_when() usando el argumento .ptype.. El bloque de código podría quedar así:

metadatos <- metadatos %>%
  mutate(GRUPO = case_when(
    publicacion > 1860 & publicacion <= 1900  ~ "Pre-1900",
    publicacion > 1900 & publicacion <= 1925 ~ "1900-25",
    publicacion > 1925 & publicacion <= 1950 ~ "1925-50",
    .default = NA_character_,
    .ptype = factor(levels = c("Pre-1900", "1900-25", "1925-50"))
  )) 

Análisis y visualización de datos

  • ¶ 68. Sería importante mencionar de dónde se pueden obtener los Woe ID. El enlace lleva a la definición, pero alguien que quisiera aplicar esto a sus propios datos quizás no sabría cómo hacerlo y es un elemento clave para poder hacer los mapas. Es decir, sería útil para quien sigue la lección entender cómo fue que se creó el archivo ciudades.xlsx y cómo podría generar uno parecido pensando en otro set de datos.
  • ¶ 68. Falta mencionar que ciudades.xlsx incluye una versión abreviada del nombre de la ciudad. Y que la razón de incluirlo es que muchas ciudades tienen nombres compuestos por más de una palabra (ejemplo, Puerto Ayora), pero los datos que tenemos de cada novela es solo a nivel de palabra, así que no es posible buscar nombres compuestos. Es importante explicitar en este punto esta limitación. También sería importante advertir que pueden existir falsos positivos por el hecho de que muchos nombres de ciudades son apellidos.
  • ¶ 69 - 70 - bloques de código. Hay dos nombres breves (Villamil y Olmedo) que están compartidos por dos ciudades. En el primer caso, las ciudades se llaman General Villamil y Puerto Villamil, pero tienen el mismo nombre abreviado. En el segundo caso, las dos ciudades se llaman Olmedo, solo que están en distintas provincias. El código escrito lo que hace es quedarse con la primera ocurrencia en orden alfabético (General Villamil, porque aparece primero en la lista que Puerto Villamil, y Olmedo de la Provincia de Manabí en vez de la de Loja). En la lección, sin embargo, no se advierte acerca de esto. Sugiero cambiar el código por uno que permita cierta flexibilidad a los usuarios en términos de cómo resolver estos duplicados. Una opción sería hacer algo así:
metadatos |> 
  left_join(novelas_frec_tokens) |> 
  filter(token %in% ciudades$Ciudad_breve) |> 
  left_join(ciudades, by = c("token" = "Ciudad_breve"), multiple = "all") 

Este bloque de código lo que hace es tomar los metadatos que tenemos, agregarle a cada novela su frecuencia de tokens, luego filtrar los tokens que coinciden con los nombres de Ciudades_breve y finalmente agregar las variables que están en el dataset de ciudades para así tener la tabla completa con todas las variables. Esta aproximación nos va a dejar con dos variables más que la forma propuesta en la lección (autor y título), que pueden ser útiles para resolver los casos de duplicados.
El último join tiene un argumento que es multiple. Ese argumento permite decidir qué hacer en casos como este, en que tenemos dos posibles ciudades para un mismo nombre abreviado. multiple = "all" (el valor por defecto) va a dejar todas las opciones, por lo que va a duplicar la información, así:

duplicados

Como tenemos los datos de autor y título, uno tendría aquí la posibilidad de revisar y decidir a cuál de las dos corresponde a partir del conocimiento que se tiene de las obras. Y así se elimina el duplicado de manera informada.
El argumento multiple tiene otras opciones: "first", "last" y "any". Eso da control sobre estos casos sobre si se quiere dejar la primera, la última, o cualquiera.
La otra ventaja de usar un join es que cuando ocurre un caso como este en que el nombre abreviado de la ciudad no es único sino que aparece repetido, la función entrega un mensaje que informa sobre esto: Detected an unexpected many-to-many relationship between x and y. Esto nos permite saber que tenemos que revisar nuestro dataset para resolver la asignación de los nombres de las ciudades.

  • ¶ 71. Sería bueno proveer el código para ver cuáles fueron las ciudades más mencionadas (Quito y Guayaquil). Por ejemplo:
ciudades_encontradas |> 
  count(token, sort = TRUE) |> 
  head()
  • ¶ 72. Sugeriría ofrecer el código para calcular la media, en caso de que alguien quiera replicar el ejercicio con sus propios datos. Lo que no me queda claro es de dónde sale el número 50000, porque si calculo la media con los datos disponible me da más cerca de 70000
> mean(ciudades_encontradas$total_volumen)
[1] 72001.25

Y la mediana está más cerca de los 60000.

> summary(ciudades_encontradas$total_volumen)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   4922   45239   63941   72001   84480  184923 

Quizás sería bueno explicar cómo se decidió que 50000 era la mejor opción.

  • ¶ 73 - bloque de código. Aunque da el mismo resultado, sugeriría hacer coincidir el orden del cálculo en el código con la forma en que se menciona en el párrafo. En él se dice: "dividir las frecuencias de las ciudades por la cantidad total de tokens en cada libro y después multiplicar esa división por 50,000". Es decir: (num_tokens / total_volumen) * 50000. En el código se multiplica primero y después se divide. Sé que el resultado es el mismo, pero ponerlo en el mismo orden puede ayudar a quienes no tienen tanta experiencia programando a leer el código con mayor facilidad.
  • ¶ 74 - bloque de código. Sugiero agregar una imagen del mapa resultante para que las personas puedan chequear que lo que les resultó es lo esperado.
  • ¶ 75 - bloque de código. Las líneas comentadas con los otros grupos pueden resultar confusas para personas con menos experiencia. Sugiero dejar solo la de Pre-1900 y decir que si se quiere hacer esto para los otros grupos habría que cambiar el valor de esa variable.
  • ¶ 75 - bloque de código. En el comentario final, #si quieres, verifica que funcionó sería bueno explicar qué es lo que debiese aparecer al ejecutar esa línea de código. O decir que si les aparece "Coordinate Reference System: NA" entonces no están los datos de coordenadas.
  • ¶ 76. Sería bueno poner una imagen de lo que debería ver la gente en este momento.
  • ¶ 77. "O podemos hacer una más elaborada, añadiendo las dos ciudades principales. " > "O podemos hacer una más elaborada, añadiendo las dos ciudades principales: Quito y Guayaquil. Para ello, necesitamos los datos de latitud y longitud de ambas ciudades".

Limitaciones y recomendaciones

  • ¶ 81. Respecto de la limitación de trabajar con cientos de volúmenes, trabajar directamente en R o en Rcommander no resuelve siempre el problema, sobre todo cuando la limitación está dada por la RAM disponible en la máquina de la persona. Si el data frame es muy muy grande (o los recursos computacionales muy pocos), una opción sería utilizar los paquetes data.table o arrow para procesar los datos. Quizás también se podría sugerir que si la tabla es demasiado grande, es útil crear versiones más pequeñas solo con los datos de interés. Por ejemplo, si el foco es un determinado período histórico, crear un nuevo objeto más pequeño solo con los datos de interés.

Eso es todo por ahora. ¡Cualquier consulta me avisan!

@JoshuaGOB
Copy link
Collaborator

Mil gracias por las revisiones tan detalladas y minuciosas, @jenniferisasi y @rivaquiroga

@jose-eduardo Ya que algunos de estos cambios requieren más tiempo que otros, podríamos fijar una fecha con suficiente tiempo para que puedas dialogar conmigo y con las revisoras. ¿Qué tal te parece el 18 de marzo? Sé que el proceso se ha retrasado(mea culpa) y me imagino que quieres terminar lo más pronto posible. Por favor déjame saber cómo puedo ayudar y te animo a aprovechar el expertise de las revisoras para aclarar cualquier duda.

Nuevamente, gracias por el trabajo excelentísimo de las revisoras.🤩 Es un privilegio trabajar con autores y revisores cómo ustedes.

@jose-eduardo
Copy link
Collaborator

jose-eduardo commented Mar 4, 2024

Gracias a tod@s por el trabajo realizado. Ha sido excelente. La próxima semana ( a partir del sábado próximo) tenemos las breves vacaciones de primavera y entonces tendré tiempo para contestar preguntas y hacer las revisiones que se requieren.

@jose-eduardo
Copy link
Collaborator

Tengo que entregar un manuscrito en 4 de abril, pero una vez lo haga regreso a este proyecto. ¡No se me ha olvidado!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: 5 Revision 2
Development

No branches or pull requests

5 participants