Toward a Literary Map of Peru: 
Mapping Sites in Vargas Llosa’s Novels

Ariana Sánchez, Wolfram Research

#WolframTechConf

Literature and Cartography

A Literary Atlas of Europe. (n.d.). Mapping and Analysing the Geography of Fiction with Interactive Tools. ETH Zürich. https://www.literaturatlas.eu/en/index.html

Casa de la Literatura Peruana. (n.d.). Mapa Literario del Centro de Lima [Literary Map of the Center of Lima]. https://www.casadelaliteratura.gob.pe/mapa-literario-lima-version-digital/

Latin America and Literature

Before 1960, Latin American literature was fragmented and colonial. The Boom of the 1960s–70s unified and popularized regional writing.

Donoso, J. (1977). The boom in Spanish American Literature: A Personal History. New York: Columbia University Press.

Volpi, J. (2008). Mentiras contagiosas [Contagious Lies]. Madrid: Páginas de Espuma.

Gotfryd, B. (1986). Mario Vargas Llosa, Peruvian author/writer, PEN Congress, NYC [Photograph]. Library of Congress, Prints & Photographs Division. https://www.loc.gov/pictures/item/2020733847/

20 novels — 17 set in or referencing Peru

Total output for this project: 3 279 pp.

Pipeline

I.  Preprocessing and Scraping

II. Curation

III. Interactive Interface

I. Preprocessing and Scraping

1. Select and clean up files

2. Import books as plaintext

bookFiles=File/@FileNames["*.pdf",booksDir]

3. Scrape locations from text

Avoid stop words

spanishStopwords=stopwords["Spanish; Castilian"];
Length[spanishStopwords]

Set linguistic patterns

(*Place indicators*)

locIndicators={"a","al","a la","a las","al este de","al este del","al este de la","al este de las","al este de los","al norte de","al norte del","al norte de la","al norte de las","al norte de los","al oeste de","al oeste del","al oeste de la","al oeste de las","al oeste de los","a los","alrededor de","alrededor del","alrededor de la","alrededor de las","alrededor de los","al sur de","al sur del","al sur de la","al sur de las","al sur de los","cerca de","cerca del","cerca de la","cerca de las","cerca de los","debajo de","debajo del","debajo de la","debajo de las","debajo de los","desde","desde el","desde la","desde las","desde los","detrás de","detrás del","detrás de la","detrás de las","detrás de los","en","encima de","encima del","encima de la","encima de las","encima de los","en el","en la","en las","en los","frente a","frente al","frente a la","frente a las","frente a los","hacia","hacia el","hacia la","hacia las","hacia los","hasta","hasta el","hasta la","hasta las","hasta los","junto a","junto al","junto a la","junto a las","junto a los","lejos de","lejos del","lejos de la","lejos de las","lejos de los","por","por el","por la","por las","por los","sobre","sobre el","sobre la","sobre las","sobre los"};

(*Normalization of place nouns*)

placeNounNormalization=\[LeftAssociation]"alameda"->"alameda","autopista"->"autopista","av"->"avenida","av."->"avenida","avenida"->"avenida","bahía"->"bahía","bosque"->"bosque","c"->"calle","c."->"calle","cabo"->"cabo","calle"->"calle","camino"->"camino","carretera"->"carretera","catedral"->"catedral","colegio"->"colegio","cordillera"->"cordillera","desierto"->"desierto","escuela"->"escuela","estadio"->"estadio","explanada"->"explanada","golfo"->"golfo","hospital"->"hospital","iglesia"->"iglesia","instituto"->"instituto","isla"->"isla","jardín"->"jardín","jirón"->"jirón","jr"->"jirón","jr."->"jirón","lago"->"lago","laguna"->"laguna","malecón"->"malecón","mar"->"mar","mercado"->"mercado","montaña"->"montaña","océano"->"océano","palacio"->"palacio","parque"->"parque","pasaje"->"pasaje","pje"->"pasaje","pje."->"pasaje","plaza"->"plaza","rio"->"río","río"->"río","ruta"->"ruta","selva"->"selva","sierra"->"sierra","teatro"->"teatro","templo"->"templo","universidad"->"universidad","valle"->"valle"\[RightAssociation];

Extract locations from their source sentences

ClearAll[extractLocations]
extractLocations[text_]:=Module[{results},
results=StringCases[text,{
(*Indicator + name*)
WordBoundary~~Alternatives@@locIndicators~~Whitespace~~loc1:RegularExpression["[A-ZÁÉÍÓÚÑ][\\wáéíóúñÁÉÍÓÚÑ-]*(\\s(de|del|la|los|las|de la|de las|de los)?\\s?[A-ZÁÉÍÓÚÑ][\\wáéíóúñÁÉÍÓÚÑ-]*){0,3}"]~~WordBoundary:>
loc1,
(*Noun + proper name*)
WordBoundary~~noun:Alternatives@@Keys[placeNounNormalization]~~Whitespace~~loc2:RegularExpression["[A-ZÁÉÍÓÚÑ][\\wáéíóúñÁÉÍÓÚÑ-]*(\\s(de|del|la|los|las|de la|de las|de los)?\\s?[A-ZÁÉÍÓÚÑ][\\wáéíóúñÁÉÍÓÚÑ-]*){0,3}"]~~WordBoundary:>(placeNounNormalization[noun]<>" "<>loc2)
},
Overlaps->True
];
results=DeleteDuplicates[cleanupWhitespaces/@StringTrim/@results];
results=Complement[results,spanishStopwords];
results=Complement[results,bannedWords];
Sort[results]
]

extractLocations["Era noviembre y comenzaba a hacer un poco de calor cuando la señora Zoila y el Chispas llevaron a don Fermín a Nueva York a que le hicieran un examen."]

II. Curation

4. Prepare the data for curation

Start constructing the data

booksData=MapThread[
Function[{places,sentences},
Map[<|"place"->#,"sentences"->Select[sentences,StringContainsQ[#]]|>&,places]
],{booksAndPlaces,bookSentences}
];

{{<|"place"->"Lima","sentences"->{"I traveled to Lima."}|>,<|"place"->"Cuzco","sentences"->{"Cuzco is beautiful."}|>},{<|"place"->"Bogotá","sentences"->{"Bogotá was lively."}|>,<|"place"->"Quito","sentences"->{"Quito sits in the Andes."}|>}}

Interpret WL entities and append the geoposition when possible

getGeoPosition[ent_Entity]:=Replace[Quiet[Check[GeoPosition[ent],Missing[],GeoPosition::missloc],GeoPosition::missloc],GeoPosition[a_]:>a]

booksData2=ResourceFunction["MonitorProgress"]@Map[Append[#,<|"coordinates"->If[Head[#entity]===Entity,getGeoPosition[#entity],Missing[]]|>]&,booksData1,{2}];

booksData2[[1,1]]

Flatten the data

booksData3=Flatten[KeyValueMap[Function[{key,val},Map[Prepend["book"->key],val]],booksData2]];

toString[f_Failure]:=$Failed
toString[ent_Entity]:=ToString[ent]
toString[{a_,b_}]:=ToString[{a,b}]
toString[m_Missing]:=$Failed

booksData4=Flatten@Map[
Function[{assoc},
Table[
<|"book"->assoc["book"],"place"->assoc["place"],"sentence"->s,"entity"->toString[assoc["entity"]],"coordinates"->toString[assoc["coordinates"]]|>,{s,assoc["sentences"]}]
],booksData3
];

<|"book"->"Book1","place"->"Amazonía","sentence"->"Dormido o desmayado, tuvo pesadillas...","entity"->"Amazonia","coordinates"->Missing[]|>

5. Export data to an .xlsx file

Total:

Length[booksData4]

6. Human review

Processed:

In Peru:

Unique locations:

III. Interactive Interface

7. Create LLM-generated summaries for each location

8. Build the interactive map

CONTACT

arianad@wolfram.com

https://www.linkedin.com/in/arianagabriela-sd/