By ZAEPAY payday loan

los que susurran

Orlando Figes. Los que susurran. La represión en la Rusia de Stalin, Edhasa, 2009, 959 páginas.
Figes, Los que susurran

Los que susurran… es un libro sobre hombres y mujeres que sufrieron, de una manera u otra, persecuciones en la Unión Soviética. Decir que se trata de crímenes del estalinismo sería quedarse cortos, porque tras la muerte de Stalin, el mundo secreto del susurro, el miedo, el desarraigo, el trauma, persisitieron. Si bien en el libro algunas biografías se destacan (como la de Konstantin Simonov, por ejemplo), el protagonismo se lo lleva el numeroso cuerpo de pequeñas biografías o biografemas, concebido a partir de archivos y entrevistas orales. El libro trata sobre la rusia de Stalin y se concentra en el largo período que va desde fines de los años veinte (con centro en el proceso conocido como deskulakización) hasta el llamado deshielo (con centro en el discurso de Kruschev en el XX congreso del PCUS sobre los crímenes de Stalin). Las grandes purgas de los años treinta y el impacto de la guerra en el ecosistema soviético son otros dos grandes hitos en el relato.
En este blog ya se han dicho algunas cosas sobre Orlando Figes. Anaclet Pons escribió un post sobre este mismo texto en el que por encima de la novedad de Los que susurran… se hacía referencia al debate acerca de la historiografía liberal y la escritura de la historia soviética en tiempos post-stalinistas o postmodernos o postparanoicos, como se prefiera. Los comentarios al post (escritos por Nicolás Pereyra y por Nes) ensayaron sendos intentos por cuestionar la escritura de Figes precisamente por su "liberalismo".
Después de leer el libro puedo opinar sobre el asunto del liberalismo de Figes (antes leí otros libros suyos, los que me gustaron y a los que ahora percibo desde otro perspectiva). Los que susurran… tiene el mismo ritmo que Anaclet Pons recuperó en su cita del comienzo del libro en el post referido. El historiador forja bisagras entre distintos relatos de familias purgadas, individuos arrojados a los campos, desposeídos, desterritorializados. Esos relatos se anidan en torno al trauma, no sólo en torno a los acontecimientos jurídicos o policiales que estuvieron en el centro de cada uno de esas pequeñas historias; es así como también leemos sobre el impacto de esos hechos, sobre la cotidianidad de los que quedaron, sobre la desestructuración de las familias a las que le han sido arrebatados uno, dos, tres miembros. Figes es un gran historiador y se mueve entre esos materiales -muchos de los cuales se pueden consultar en orlandofiges.com- con notable pericia.
Se trata, sin embargo, de una narración que hereda quizás demasiado de Archipiélado Gulag, un libro que con esa poética activó distintas reflexiones políticas cuando fue difundido. Exagerando, podría decirse que Los que susurran… es una versión del archipiélago, una mirada sobre los estragos del estalinismo en el corazón del domus, y a la vez una mirada que pretende liberarse del tono solzhenitzin, de la toma de partido. Pero al denunciar las incursiones del poder en el entramado civil a través de esa "reconstrucción" testimonial, Figes parece considerar al tejido civil naturalmente liberado de la política.
Por esas dos cosas es que, más o menos por la mitad de libro, me empecé a preguntar si esa estrategia narrativa que explota el testimonio para inducir una conclusión acerca del estalinismo in toto, finalmente no devenía un límite para los objetivos del texto. El libro no tiene el propósito de desarrollar esa red de trazas menores ni de salirse del archivo de casos para discutir "la rusia de Stalin". Así se sostiene hasta el final. Y eso se debe a que la razón que anima el armado del puzzle historiográfico, la lógica que escribe las bisagras, es anterior, es apriori. Y ese punto, que liga a Figes con El archipiélago… pero también a Figes con el "liberalismo" del que se le acusa, es algo que empieza a arruinar a esas voces inolvidables, que deteriora ese tejido hecho de penas e injusticias, porque al imponerles una causa externa (Stalin, los totalitarismos, etc.) menoscaba la lucha de esos hombres y mujeres por vivir el mapa kafkiano del poder pero también el del departamento comunal, el del pasillo, el del barrio poblado de "enemigos del pueblo", denunciantes, delatores, chicas del komsomol con padres kulaks, huérfanos, soplones, soplones soplados, relapsos, ascetas y vencidos. Al poner, Figes, esos testimonios como prueba de algo que se da por existente o de algo que se constituye casi exclusivamente a partir de la persecución de gentes, reduce cuantiosamente su archivo. Figes entiende claramente los problemas de la historia oral para dar cuenta de procesos históricos (aunque prefiere esos métodos al analisis de obras literarias) pero si tales inconvenientes caben en los complejos procedimientos de la memoria y el olvido, también nos llegan del cuestionario con el que se interpretan los dichos o gestos ajenos. El libro de Figes se hace cada vez menos complejo a medida que avanzamos en él debido precisamente a que la repetición de una estrategia casi judicial tiene como base una explicación alóctona (aunque con ella no pretenda exculpar).
Los que susurran… multiplica nuestras preguntas, abre zonas oscuras, litigios regionales entre el trauma y la felicidad. Nos muestra, Figes, pequeñas ventanas ominosas de una vida cotidiana que se parece un poco a las del lager pero más a la que conocemos. Nos las muestra un poco, digo, pero también las apaga, las somete a una explicación que no quiere saber nada de cada una de ellas. Es sobre ese desconcierto entre lo conocido y lo conocido ominoso sobre el que podemos operar mejor con libros como el de Richard Stites, Revolutionary Dreams… (incluso en su acendrado antiestalinismo) o el de Susan Buck Morss, Mundo Soñado y catástrofe… Una zona que tiene relación con el poder con mayúsculas pero no sólo porque lo padece, sino porque lo anima y es animada por él. El libro de Figes termina con un fragmento de una entrevista en donde alguien se enorgullece de poder decir su descendencia kulak. Se trata de una liberación, de una catarsis, pero en cierto modo se trata también de poner a esa figura como resistente, a la víctima como contradictor. Al devolverle una entidad política que a lo largo del libro el propio Figes le había negado (kulak era mayormente una figura de acusación, un modo conspirativo de extirpar a quienes tenían algunas posesiones o hablaban en voz alta sobre la realidad), Figes nos revela su enemigo y los límites del argumento que opone la libertad del individuo a los regímenes socialistas. Al menos los límites de esa oposición para abordar la pequeña dimensión, la cocina nimia de las micropolíticas.

the Programming Historian, parte IV

Seguimos con la traducción de algunos capítulos del libro The Programming Historian, de William J. Turkel, Adam Crymble y Alan MacEachern.
Ahora vamos con el capítulo 5.

Calculando frecuencias

Medidas usuales para textos

En la sección anterior, escribimos un programa en Python al que llamamos html-a-lista-1.py . Dicho programa sirve para bajar una página web a la que le quita el formateo HTML y los metadata, y luego nos devuelve una lista de “palabras”, como esta:

 ['Dictionary', 'of', 'Canadian', 'Biography', 'DOLLARD', 'DES', 
'ORMEAUX', '(called', 'Daulat', 'in', 'his', 'death', 'certificate', 
'and', 'Daulac', 'by', 'some', 'historians),', 'ADAM,', 'soldier,',
'\x93garrison', 'commander', 'of', 'the', 'fort', 'of', 
'Ville-Marie', '[Montreal]\x94;', 'b.', '1635,', 'killed', 'by', 
'the', 'Iroquois', 'at', 'the', 'Long', 'Sault', 'in', 
'May 1660.', '\xa0\xa0\xa0\xa0\xa0', 'Nothing', 'is', 'known', 
'of', 'Dollard\x92s', 'activities', 'prior', 'to', 'his', 'arrival', 
'in', 'Canada', 'except', 'that', '\x93he', 'had', 'held', 'some', 
'commands', 'in', 'the', 'armies', 'of', 'France.\x94', 'Having', 
'come', 'to', 'Montreal', 'as', 'a', 'volunteer,', 'very', 
'probably', 'in', '1658,', 'he', 'continued', 'his', 'military', 
'career', 'there.', 'In', '1659', 'and', '1660', 'he', 'was', 
'described', 'as', 'an', '\x93officer\x94', 'or', '\x93garrison', 
'commander', 'of', 'the', 'fort', 'of', 'Ville-Marie,\x94', 'a', 
'title', 'that', 'he', 'shared', 'with', 'Pierre', 'Picot\xe9', 
'de', 'Belestre.', 'We', 'do', 'not', 'however', 'know', 'what', 
'his', 'particular', 'responsibility', 'was.']

Debido a que ya sabemos leer, esa habilidad del programa, por sí misma, no es muy importante. Pero podemos usar el texto para hacer cosas que usualmente no son posibles sin un software especializado. Vamos a empezar calculando las frecuencias de palabras y de otras unidades lingüísticas.

Limpiando la lista

Está claro que nuestra lista necesitará un poco de limpieza antes de que podamos usarla para contar frecuencias. Antes que nada, no queremos saber las frecuencias atadas a mayúsculas o minúsculas: “Dollard” y “DOLLARD” deberían ser contadas como la misma palabra. Normalmente las palabras son clasificadas en minúsculas cuando contamos frecuencias, por lo que está bien utilizar el método de cadena lower.

print('Hola MUNDO'.lower())
-> hola mundo

Hay distintas marcas de puntuación que nos cambiarían el conteo de frecuencia si las incluiríamos en él. Queremos que “soldado:” sea contabilizada como “soldado” y “[Montreal]” como “Montreal”. Mirando en la impresión en el panel de salida del Komodo, encontramos también “&nbsp ;” que es el código HTML para el ampersand. Utilizando otro método de cadena, podemos reemplazar ese código con un espacio en blanco, de la siguiente manera:

print('hola mundo')
-> hola mundo
 
print('hola mundo'.replace(' ',' '))
-> hola mundo

Hay también un grupo de caracteres acentuados franceses que son representados con cadenas Unicode como “\xe9” (que indica “é”). Aprenderemos más acerca de trabajar con caracteres Unicode más tarde; por ahora vamos a dejarlos como están.
En este punto, podríamos mirar en otras entradas DCB [de ese diccionario se extrajo la página web del ejemplo] y en un rango más amplio de potenciales fuentes de documentos para asegurarnos que no haya otros caracteres especiales que nos traigan problemas más adelante. Podríamos también anticipar situaciones donde no queremos quitar la puntuación (por ejemplo, el signo que distingue un monto de dinero como en “$1629” de un año, o el que distingue el sentido de “1629-40” del otro en “1629 40”). Esto es lo que hace que los programadores profesionales cobren: tratar de pensar todo lo que puede estar mal por adelantado.

Vamos a encarar otra aproximación. Nuestro principal objetivo es desarrollar técnicas que un historiador o una historiadora puedan utilizar durante su investigación. Esto significa que siempre vamos a preferir la solución correcta que pueda desarrollarse rápidamente. Más que dedicar tiempo a hacer nuestro programa robusto frente a excepciones, simplemente vamos a descartar todo aquello que no sea una letra acentuada o sin acentuar o un número arábigo. La programación es normalmente un proceso de refinamiento por etapas. Comenzamos con un problema y con una solución parcial, y entonces procedemos a refinar nuestra solución hasta hacer algo que funcione mejor.

Nuestro primer uso de expresiones regulares.
A los efectos de eliminar caracteres especiales, vamos a hacer uso de un poderoso mecanismo llamado expresiones regulares. Las expresiones regulares son facilitadas por muchos lenguajes de programación de distintas maneras. Para hacer lo que queremos ahora, tenemos que importar la biblioteca de expresiones regulares de Python y compilar un patrón que encuentre cualquier cosa que no sea un carácter alfanumérico. Copie la siguiente función y péguela en el módulo dh.py.

# Dada una cadena de texto, remueva todos los caracteres alfanuméricos
# (utilizando la definición Unicode de alfanumérico).
 
def stripNonAlphaNum(texto):
    import re
    return re.compile(r'\W+', re.UNICODE).split(texto)

La expresión regular en el código de más arriba es \W+. La \W es la forma abreviada para significar la clase de caracteres alfanuméricos. En las expresiones regulares de Python, el signo más (+) encuentra una o más copias de un caracter determinado. El re.UNICODE le dice al intérprete que queremos incluir caracteres de otros idiomas en nuestra definición de alfanumérico, además de las series que van de la A a la Z, de la a a la z, y del / al 9 [N.T: el primer set de caracteres ASCII era etnocéntrico y no incluía signos como las vocales acentuadas o la cedilla; en el texto original las series enumeradas al final de la última oración son del inglés; y todo lo que queda fuera, parte de “los otros idiomas del mundo”]. Las expresiones regulares deben ser compiladas antes de ser usadas, que es lo que el resto de las órdenes del programa hace. No se preocupe por entender el resto de la compilación por ahora.
Al refinar nuestro programa html-a-lista, podemos dejarlo así:

# html-a-lista-2.py
 
import urllib2
import dh
 
url = 'http://niche.uwo.ca/programming-historian/dcb/dcb-34298.html'
 
response = urllib2.urlopen(url)
html = response.read()
texto = dh.stripTags(html).replace(' ', ' ')
wordlist = dh.stripNonAlphaNum(texto.lower())
print wordlist[0:500]

Cuando ejecute el programa y mire el panel de salida del Komodo, podrá ver que ha hecho un mejor trabajo. Como esperábamos, dejó los caracteres acentuados en forma de códigos (así, palabras como “Picoté” aparecen como “picot\xe9”). Dividió los términos con guiones, como “Ville-Marie” en dos palabras, y cambió el posesivo “s” en una palabra separada, dejando fuera el apóstrofo. Esta es una buena aproximación a lo que queríamos hacer, así que podemos avanzar a contar frecuencias antes de intentar hacer este programa mejor. (Si usted trabaja con fuentes en más de un idioma necesitará aprender más acerca del estándar Unicode y acerca del soporte de Python para ese estándar.

Diccionarios de Python

Tanto las cadenas como las listas están secuencialmente ordenadas, lo que quiere decir que podemos acceder a sus contenidos utilizando un index, un número que comienza con 0. Si tiene una lista que contiene cadenas, puede usar un par de índices para acceder, primero, a una cadena en particular, y luego a un caracter específico en esa cadena. Estudie el siguiente ejemplo:

s = 'hola mundo'
print s[0]
-> h
 
print s[1]
-> o
 
m = ['hola', 'mundo']
print m[0]
-> hola
 
print m[1]
-> mundo
 
print m[0][1]
-> o
 
print m[1][0]
-> m

Para llevar la cuenta de las frecuencias, necesitaremos otro tipo de objeto en Python, un diccionario. El diccionario es una colección desordenada de objetos. Esto significa que no podemos usar un índice para recuperar un elemento del mismo. Podemos hacerlo, de todos modos, utilizando una clave (key). Estudie el siguiente ejemplo:

d = {'mundo': 1, 'hola': 0}
print d['hola']
-> 0
 
print d['mundo']
-> 1
 
print d.keys()
-> ['mundo', 'hola']

Fíjese que usamos llaves para definir un diccionario pero corchetes para acceder a las cosas que hay en él. Las operaciones con claves devuelven una lista de claves que están definidas en el diccionario.

Conteo de frecuencia de palabras

Ahora vamos a contar la frecuencia de cada palabra en nuestra lista. Ya vimos que esto es fácil procesar una lista utilizando un bucle for. Pruebe guardando y ejecutando este ejemplo:

# contar-lista-items-1.py
 
wordstring = 'quien mal anda mal acaba '
wordstring += 'dime con quien andas y te dire quien eres'
 
wordlist = wordstring.split()
 
wordfreq = []
for word in wordlist:
    wordfreq.append(wordlist.count(word))
 
print "Cadena\n" + wordstring +"\n"
print "Lista\n" + str(wordlist) + "\n"
print "Frecuencia\n" + str(wordfreq) + "\n"
print "Pares\n" + str(zip(wordlist, wordfreq))

Obtendrá algo como esto:

Cadena
quien mal anda mal acaba dime con quien andas y te dire quien eres
Lista
['quien', 'mal', 'anda', 'mal', 'acaba', 'dime', 'con', 'quien', 'andas', 'y', 'te', 'dire', 'quien', 'eres']
Frecuencia
[3, 2, 1, 2, 1, 1, 1, 3, 1, 1, 1, 1, 3, 1]
Pares
[('quien', 3), ('mal', 2), ('anda', 1), ('mal', 2), ('acaba', 1), ('dime', 1), ('con', 1), ('quien', 3), ('andas', 1), ('y', 1), ('te', 1), ('dire', 1), ('quien', 3), ('eres', 1)]

En ese programa, empezamos con una cadena y la dividimos en una lista, como ya lo habíamos hecho antes. Entonces recorrimos cada palabra de esa lista y contamos las veces que esa palabra aparece en toda la lista. Luego agregamos el resultado a una lista de frecuencias de palabras. Usando el comando zip podemos comparar la primera palabra de la lista de palabras con el primer número de la lista de frecuencias, la segunda palabra con la segunda frecuencia y así sucesivamente. Finalizamos obteniendo una lista de pares (palabras y frecuencias). La declaración str convierte cualquier objeto a una cadena, para poder imprimirlo.
Python incluye una herramienta muy conveniente llamada lista comprensiva (list comprehension), que podemos usar para hacer lo mismo que con el bucle loop pero más económicamente.

# contar-lista-items-2.py
wordstring = 'quien mal anda mal acaba '
wordstring += 'dime con quien andas y te dire quien eres'
wordlist = wordstring.split()
wordfreq = [wordlist.count(w) for w in wordlist]
print "Cadena\n" + wordstring +"\n"
print "Lista\n" + str(wordlist) + "\n"
print "Frecuencias\n" + str(wordfreq) + "\n"
print "Pares\n" + str(zip(wordlist, wordfreq))

En este punto tenemos una lista de pares, donde cada par contiene una palabra y su frecuencia. Note que esta lista podría ser redundante. Si el artículo “el” se repitiera 500 veces, la lista contendría quinientas copias de ese par (‘el’, 500). La lista está, además, ordenada por el orden de las palabras en el texto inicial. Podemos resolver ambos problemas convirtiéndola en un diccionario. Entonces todo lo que tenemos que hacer es imprimir el diccionario en orden, desde los ítems más frecuentes a los menos frecuentes.

De HTML a un diccionario de pares palabra-frecuencia.

A partir de lo que tenemos hasta ahora, queremos una función que pueda convertir una lista de palabras en un diccionario de pares de palabra-frecuencia. La única declaración nueva que necesitamos conocer es dict, que hacer un diccionario a partir de una lista de pares. Agregue el siguiente código al módulo dh.py.

# Dada una lista de palabras, devuelva un diccionario de
# pares de palabra-frecuencia.
 
def wordListToFreqDict(wordlist):
    wordfreq = [wordlist.count(p) for p in wordlist]
    return dict(zip(wordlist,wordfreq))

Vamos a querer además un función que pueda ordenar un diccionario de pares de palabra-frecuencia por frecuencias, de modo descendiente. Copie también esto al módulo dh.py:

# Ordenar un diccionario de pares de palabra-frecuencia por
# frecuencias de modo descendiente
 
def sortFreqDict(freqdict):
    aux = [(freqdict[key], key) for key in freqdict]
    aux.sort()
    aux.reverse()
    return aux

Podemos ahora escribir un programa que tome una URL y devuelva pares de palabras-frecuencia para la página web, ordenados de acuerdo a la frecuencia de cada palabra, desde la que más tiene a la que menos tiene. Copie el siguiente programa en el Komodo, guarde como html-to-freq.py y ejecútelo. Estudie cuidadosamente el programa y lo que se imprime en el panel de salida, antes de continuar.

# html-a-freq.py
 
import urllib2
import dh
 
url = 'http://niche.uwo.ca/programming-historian/dcb/dcb-34298.html'
 
response = urllib2.urlopen(url)
html = response.read()
text = dh.stripTags(html).replace(' ', ' ')
wordlist = dh.stripNonAlphaNum(text.lower())
dictionary = dh.wordListToFreqDict(wordlist)
sorteddict = dh.sortFreqDict(dictionary)
for s in sorteddict: print str(s)

Removiendo las stop words

Cuando miramos la salida de nuestro programa html-a-freq.py, vemos que las mayores frecuencias son para palabras como “the”, “of”, “and”.

(647, 'the')
(310, 'of')
(273, 'to')
(202, 'and')
(171, 'in')
(134, 'a')
(118, 'that')
(91, 'dollard')
(78, 'was')
(78, 'their')
(75, 'were')
(72, 'they')
(71, 'his')

Esas palabras son las más usuales en cualquier texto en inglés, y no nos dicen mucho sobre la biografía de Dollard. En general, estamos más interesados en encontrar las palabras que nos permitan distinguir este texto de otros con diferentes temas. Por lo que vamos a filtrar, a quitar, las palabras más comunes. Las palabras que son ignoradas, como esas, se conocen como stop words. Vamos a usar una lista, adaptada de un post de unos cientistas de Glasgow. Copie lo siguiente en el comienzo de la biblioteca dh.py.

stopwords = ['a', 'about', 'above', 'across', 'after', 'afterwards']
stopwords += ['again', 'against', 'all', 'almost', 'alone', 'along']
stopwords += ['already', 'also', 'although', 'always', 'am', 'among']
stopwords += ['amongst', 'amoungst', 'amount', 'an', 'and', 'another']
stopwords += ['any', 'anyhow', 'anyone', 'anything', 'anyway', 'anywhere']
stopwords += ['are', 'around', 'as', 'at', 'back', 'be', 'became']
stopwords += ['because', 'become', 'becomes', 'becoming', 'been']
stopwords += ['before', 'beforehand', 'behind', 'being', 'below']
stopwords += ['beside', 'besides', 'between', 'beyond', 'bill', 'both']
stopwords += ['bottom', 'but', 'by', 'call', 'can', 'cannot', 'cant']
stopwords += ['co', 'computer', 'con', 'could', 'couldnt', 'cry', 'de']
stopwords += ['describe', 'detail', 'did', 'do', 'done', 'down', 'due']
stopwords += ['during', 'each', 'eg', 'eight', 'either', 'eleven', 'else']
stopwords += ['elsewhere', 'empty', 'enough', 'etc', 'even', 'ever']
stopwords += ['every', 'everyone', 'everything', 'everywhere', 'except']
stopwords += ['few', 'fifteen', 'fifty', 'fill', 'find', 'fire', 'first']
stopwords += ['five', 'for', 'former', 'formerly', 'forty', 'found']
stopwords += ['four', 'from', 'front', 'full', 'further', 'get', 'give']
stopwords += ['go', 'had', 'has', 'hasnt', 'have', 'he', 'hence', 'her']
stopwords += ['here', 'hereafter', 'hereby', 'herein', 'hereupon', 'hers']
stopwords += ['herself', 'him', 'himself', 'his', 'how', 'however']
stopwords += ['hundred', 'i', 'ie', 'if', 'in', 'inc', 'indeed']
stopwords += ['interest', 'into', 'is', 'it', 'its', 'itself', 'keep']
stopwords += ['last', 'latter', 'latterly', 'least', 'less', 'ltd', 'made']
stopwords += ['many', 'may', 'me', 'meanwhile', 'might', 'mill', 'mine']
stopwords += ['more', 'moreover', 'most', 'mostly', 'move', 'much']
stopwords += ['must', 'my', 'myself', 'name', 'namely', 'neither', 'never']
stopwords += ['nevertheless', 'next', 'nine', 'no', 'nobody', 'none']
stopwords += ['noone', 'nor', 'not', 'nothing', 'now', 'nowhere', 'of']
stopwords += ['off', 'often', 'on','once', 'one', 'only', 'onto', 'or']
stopwords += ['other', 'others', 'otherwise', 'our', 'ours', 'ourselves']
stopwords += ['out', 'over', 'own', 'part', 'per', 'perhaps', 'please']
stopwords += ['put', 'rather', 're', 's', 'same', 'see', 'seem', 'seemed']
stopwords += ['seeming', 'seems', 'serious', 'several', 'she', 'should']
stopwords += ['show', 'side', 'since', 'sincere', 'six', 'sixty', 'so']
stopwords += ['some', 'somehow', 'someone', 'something', 'sometime']
stopwords += ['sometimes', 'somewhere', 'still', 'such', 'system', 'take']
stopwords += ['ten', 'than', 'that', 'the', 'their', 'them', 'themselves']
stopwords += ['then', 'thence', 'there', 'thereafter', 'thereby']
stopwords += ['therefore', 'therein', 'thereupon', 'these', 'they']
stopwords += ['thick', 'thin', 'third', 'this', 'those', 'though', 'three']
stopwords += ['three', 'through', 'throughout', 'thru', 'thus', 'to']
stopwords += ['together', 'too', 'top', 'toward', 'towards', 'twelve']
stopwords += ['twenty', 'two', 'un', 'under', 'until', 'up', 'upon']
stopwords += ['us', 'very', 'via', 'was', 'we', 'well', 'were', 'what']
stopwords += ['whatever', 'when', 'whence', 'whenever', 'where']
stopwords += ['whereafter', 'whereas', 'whereby', 'wherein', 'whereupon']
stopwords += ['wherever', 'whether', 'which', 'while', 'whither', 'who']
stopwords += ['whoever', 'whole', 'whom', 'whose', 'why', 'will', 'with']
stopwords += ['within', 'without', 'would', 'yet', 'you', 'your']
stopwords += ['yours', 'yourself', 'yourselves']

Ahora que pusimos esas palabras en una lista es fácil utilizarlas. Copie esta función también en el módulo dh.py.

# Dada una lista de palabras remueva cualquiera 
# que figure en la lista de stop words.
 
def removeStopwords(wordlist, stopwords):
    return [w for w in wordlist if w not in stopwords]
[Para ampliar este tema ver la solapa discussion en el wiki]

Poniendo todo junto

Ahora tenemos todo lo que necesitamos para determinar las frecuencias de palabras en páginas web. Copie lo siguiente en el Komodo, guárdelo como html-a-freq-2.py y ejecútelo.

# html-to-freq-2.py
 
import urllib2
import dh
 
url = 'http://niche.uwo.ca/programming-historian/dcb/dcb-34298.html'
 
response = urllib2.urlopen(url)
html = response.read()
text = dh.stripTags(html).replace(' ', ' ')
fullwordlist = dh.stripNonAlphaNum(text.lower())
wordlist = dh.removeStopwords(fullwordlist, dh.stopwords)
dictionary = dh.wordListToFreqDict(wordlist)
sorteddict = dh.sortFreqDict(dictionary)
for s in sorteddict: print str(s)

Si todo va bien, la salida debería mostrar algo así:

(91, 'dollard')
(64, 'iroquois')
(33, 'long')
(27, 'sault')
(24, 'enemy')
(24, '1660′)
(20, 'time')
(20, 'seventeen')
(20, 'french')
(19, 'new')
(19, 'montreal')
(19, 'army')
(18, 'hurons')
(18, 'fort')
(17, 'france')
(15, 'men')
(14, 'marie')
(14, 'companions')

Canton, tomo VI

Darío Cantón. De la misma llama. Nue-Car-Bue. Tomo VI 1928-1960, Libros del Zorzal, 2008.
Nue-Car-Bue

En las últimas páginas de este libro, cuando alcanzamos el año 1960, Canton transcribe un poema de Corrupción de la naranja y allí está dicho: “papeles/para hacer un nombre”. Y ese podría ser un acápite para la presentación de Nue-Car-Bue, el tomo seis de las memorias de Darío Canton, porque como ya ha sido señalado, la notable diversidad e intensidad de los materiales presentados en el libro puede llevarnos rápidamente a pensar acerca de la colección, acerca del coleccionista. Acerca de un archivo de narradores distintos y alternos, de parientes, amigos, conocidos y citados, de distintos Darío Canton (el de la época, el que escribe, el que edita); una colección compuesta de recetas, fotos, álbumes, mapas, publicidades, etc. No es infrecuente leer o escuchar versiones de la fascinación por la colección, por el archivo, en entrevistas o reseñas o críticas de los tomos ya publicados de la autobiografía de Canton. Pero si bien el archivo y la colección son modos materialistas de tratar la experiencia (la poliglosia, lo dialógico, etc.) son también dos modulaciones cercanas a la historia anticuaria, disciplina que ya no veneramos. Había un método allí pero se me ocurre que no es el método Canton, por lo que esta lectura de Nue-Car-Bue debe comenzar por indicar que el poderoso influjo de los vastos papeles es una entrada que desistiremos de hacer: hay muchos tipos de traficantes de antigüedades pero Canton no encaja en ninguno de ellos.

Porque hay en los textos autobiográficos de Canton, como también ha sido indicado, una filosofía de la composición. Y comienza con una certeza, expresada de distintos modos en sus escritos: cuando alguien pacta con algunas preguntas, ese pacto es a perpetuidad. Se trata de una certeza inquietante porque a poco de pensarla advertimos que carga contra sí misma. En Canton esos interrogantes trabajan sobre el entre-lugar, sobre las transiciones. Así el modo de contraponer borradores y versiones finales de poemas intenta reponer el tiempo sellado de la producción de los sentidos de la obra. Busca liberar al poema del fetichismo de la creación, instalar su contingencia. También Corrupción de la naranja remite a especulaciones sobre los cambios (“¿cuándo algo dejó de ser lo que era?”, se pregunta el poeta). Y está la extensa autobiografía de Canton, y en ella este tomo, sobre los que ronda aquella idea de Luisa Passerini acerca de la posibilidad de inscribir en una biografía los cambios que el propio biógrafo padeció entre el comienzo de la investigación y el final de la misma. El acápite que en principio debió tratar sobre los muchos papeles, en este segundo momento, trata sobre un modo de mirar, y ese modo de mirar está ávido de un corpus notable de restos del pasado pero se asfixia entre las cosas porque su perspectiva interroga a esas fragmentos, a esos añicos, acerca de su variación, acerca de sus historias. La idea de colección propone un universo cerrado; el trabajo de Canton a partir de preguntas sobre las mutaciones, uno abierto. Distintos soportes y materiales abundan en este libro pero su enumeración no es, pienso, la mejor aproximación a esa economía de la transformación que consiste en atacar con preguntas los objetos de la historia anticuaria hasta volverlos contra sí, hasta apagar su monumental existencia. La carga que el autor asesta contra el principio de sola scriptura, contra la escritura de clausura se advierte en todos sus textos autobiográficos. En gran medida la correspondencia entre los lectores de Asemal y Canton (autor, editor, distribuidor de la revista) es también parte de ese pacto a perpetuidad porque importa menos el gesto sociológico de consultar a los lectores acerca de la lectura que la certeza de que el ciclo vital del poema no se clausura con la versión publicada del mismo.

Ese método Canton por momentos se viste con atuendos sociológicos, utiliza las artes de la digresión en las notas al pie, y el recurso de los posdatados, en fin, rebusca en las técnicas de las sociales. Pero como parte de preguntas que se obligan a sí mismas a la recursividad, hace de esos materiales oficiosos, instrumentos de una voz que apenas comenzamos a leer Nue-Car-Bue sabemos justa. Y entonces, además de un método, la lectura de la autobiografía de Canton cede una voz.

En algún lugar del libro, Cantón recuerda un acontecimiento hasta ese momento inédito, un hecho que lo predispuso a la escritura, a la narración sincera y luego de escribirlo dice sobre ese suceso: “Herida que sería como la justificación de este proyecto en el que estoy, la prueba de que me acerco a su fin, de que he alcanzado a escribir lo que jamás me había atrevido a repetir”, y esa voz que irrumpe en la escena recordada, que viene del entre-lugar, del año ’89 –momento de producción del texto– , entre el recuerdo de la infancia y el momento de la lectura, esa es la voz Canton. Una voz coloquial. Ha perdido lo ampuloso del que cree que tiene algo para decir. Y como fondo, en sordina o a contravoz están esas preguntas fáusticas , tal como habitan en la conversación de los compinches, en la de los animados.

El sedimento de las preguntas con las que Canton pactó a perpetuidad hace de su voz, una voz de búsqueda. Los períodos que recorre, marcados por su paso por distintas etapas de crecimiento (niñez, escuela primaria, secundaria, etc.), no sufren el apisonado que provocan las narrativas de autoridad. (Me refiero a modos de tramar el pasado del autobiógrafo que van desde poner en la vida pasada lo que sólo puede conocerse desde el presente hasta el de narrar sencillamente el mundo, la vida social, el pasado en toda su magnificencia colectiva pero sin mencionar el denso punto individual.)

Viajamos por distintas geografías de la región del hombre a través de la incertidumbre y la empatía, con esa voz que en lugar de responder los interrogantes del pasado, les cede un sentido. Pienso que no hay mejor forma de tratar con los archivos que la que ostenta un modo de lastimarlos o conjurarlos.

Nue-Car-Bue, entonces, no es depósito de baluartes de una trayectoria de vida, sino una poética de la transformación, un reservorio de preguntas, una conversación sostenida. Debería buscar en alguna variación del quijote un epígrafe para este texto, pero todavía no es tarde; aún no concluye De la misma llama, puedo esperar al próximo tomo para eso.

the Programming Historian, parte III

Seguimos con la traducción de algunos capítulos del libro The Programming Historian, de William J. Turkel, Adam Crymble y Alan MacEachern.
Ahora vamos con el capítulo 5.

Del HTML a una lista de palabras


Quitando el formato HTML

A menudo estamos interesados en conservar el contenido textual de una fuente online para procesarla, pero para ello tenemos que deshacernos de las etiquetas y metadata HTML. Vamos a empezar haciéndolo de una manera rápida y desprolija. En el lenguaje HTML que vimos hasta ahora hay unos tipos básicos de etiquetas. Para quitar todas ellas parece seguro ignorar todo lo que esté entre un par de símbolos menor y mayor (< como apertura y > como clausura).


Nuestro algoritmo sera algo así:
1. Comenzar con una cadena (string) vacía (de nombre html para guardar nuestro texto en ella.
2. Mirar cada carácter en la cadena html, uno a uno.
3. Si el carácter es un signo menor (<) estamos entrando a una etiqueta por lo tanto ignoramos el caracter.
4. Si el carácter es un signo mayor (>) estamos abandonando una etiqueta.
5. Si estamos dentro de la etiqueta, ignoramos el carácter, de otro modo lo agregamos a la cadena de texto.
Un algoritmo es un procedimiento especificado en detalle que puede ser implementado por una computadora. Lo implementaremos ahora.
Más sobre cadenas en Python
Ya vimos que hay dos maneras de delimitar cadenas: usando comillas simples o dobles.

mensaje1 = 'hola mundo'
mensaje2 = "hola mundo"

Python posee un tercer tipo de cadena que admite múltiples líneas. Nos será útil más adelante.

mensaje3 = """hola
hola 
hola mundo"""

Python incluye muchos comandos para manipular cadenas de textos. Si quiere experimentar con esos comandos, puede escribir y ejecutar pequeños programas como hacemos nosotros, o bien puede abrirlos en la interfase de Python.

Algunas de las explicaciones en las siguientes secciones probablemente deban ser ampliadas un poco. Con ese fin puede consultarse la página de discusión del wiki.



Usted puede concatenar cadenas utilizando el operador de adición (+). Observe que debe ser explícito acerca de la cantidad y la ubicación de los espacios en blanco que desea en la cadena. También puede crear múltiples copias de cadenas usando el operador de multiplicación (*).

mensaje4 = 'hola' + ' ' + 'mundo'
print mensaje4
 
-> hola mundo
 
mensaje5a = 'hola ' * 3
mensaje5b = 'mundo'
print mensaje5a + mensaje5b
 
-> hola hola hola mundo

¿Y si quiere agregar sucesivamente material al final de cada cadena? Hay un operador especial para eso.

mensaje6 = 'hola'
mensaje6 += ' '
mensaje6 += 'mundo'
print mensaje6
 
-> hola mundo

Podemos determinar el número de caracteres de un cadena utilizando len. Observe que los espacios en blanco se cuentan como caracteres.

mensaje7 = 'hola' + ' ' + 'mundo'
print len(mensaje7)
 
-> 10

Finalmente, en algunas situaciones necesitará incluir comillas de distinto tipo en una cadena, y no querrá que el intérprete Python se haga una idea equivocada y finalice la cadena cuando se cruce con uno de esos caracteres. En Python, usted puede poner una barra inversa delante de un comilla para que indique que con ella no termina la cadena. Esas marcas son conocidas como secuencias de escape.

print '\"'
 
-> "
 
print 'El programa imprimió \"hola mundo\"'
 
-> El programa imprimió "hola mundo"

Otras dos secuencias de escape permiten imprimir tabulaciones y saltos de línea:

print 'hola<strong>\t</strong>hola<strong>\t</strong>hola<strong>\n</strong>mundo'
 
->hola     hola     hola 
mundo

Para volver a nuestro algoritmo, primero tenemos que crear una cadena vacía para poner el texto allí.

texto = ''

Ok, eso fue fácil. Sabemos como agregar caracteres en esa cadena cuando los necesitemos:

texto += char

Looping

Ahora necesitamos un modo de mirar cada carácter en la cadena html. Como muchos otros lenguajes de programación, Python incluye varios mecanismos de looping (bucles). El que queremos se llama bucle for. El ejemplo de abajo le dice al intérprete que haga algo por cada carácter en la cadena denominada html. En efecto, eso implica crear una cadena de un solo carácter bautizada char, la que contendrá cada carácter de la cadena html en sucesión.

for char in html:
    # aquí hacemos algo con char

Branching [condicionales]

Lo siguiente que necesitamos es un modo de testear los contenidos de una cadena y elegir un curso de acción en base a esa prueba. Otra vez, como muchos lenguajes de programación, Python cuenta con mecanismos de branching. La que usaremos más abajo se llama sentencia condicional if. Le pedimos que mire si la cadena char que le pasamos contiene un signo menor.

if char == '<':
    # hacemos algo

Un modo más común del condicional if nos permite especificar qué hacer si el test es falso.

if char == '<':
    # hacemos algo 
else:
    # hacemos algo distinto

En Python tenemos la opción de hacer tests supletorios luego del primero, usando el comando elif (que es una forma abreviada de else if).

if char == '<':
    # hacemos algo
elif char == '>':
    # hacemos otra cosa
else:
    # hacemos algo completamente diferente.

Para evitar confusiones, Python usa un signo igual simple (=) para asignar, que puede entender como establecer la igualdad de una cosa con cualquier otra. Con el fin de probar la igualdad usa el signo igual doble (==). A menudo los que recién comienzan a programar confunden ambos signos.
¿Cómo podemos saber cuándo estamos dentro de un tag del HTML? Podemos usar una variable numérica que llamaremos inside, la que será 1 (verdad) si estamos dentro de una ramificación y 0 (falso) si no lo estamos.

La rutina para quitar etiquetas de HTML

Para poner todo en un mismo paquete, la versión final de nuestra rutina se muestra más abajo. Copie ese código y péguelo en el Komodo. Guárdelo en un archivo llamado dh.py. Este archivo será el que contenga todo el código que nosotros queremos reutilizar. En otras palabras, dh.py es un módulo. (Más sobre esto puede hallarse en la página de discusión del wiki.)

# Dada una cadena que contiene HTML, remover todos los caracteres
# que se encuentren entre un signo menor y uno mayor, incluyendo
# esos signos.
 
def stripTags(html):
inside = 0
texto = ''
for char in html:
   if char == '<':
   inside = 1
   continue
elif (inside == 1 and char == '>'):
   inside = 0
   continue
elif inside == 1:
   continue
else:
   texto += char
return texto

Como puede leerse en el código, nos enteramos que necesitamos una orden más para hacer que el código funcione. El comando de Python continue le dice al intérprete que salte al inicio del bucle. Por lo que si el carácter es un signo menor quedará asentado que estamos dentro de una etiqueta y ahí mismo terminamos el procesamiento de ese carácter. Necesitamos, en ese punto, continuar con el siguiente en la cadena html antes que continuar procesando un carácter que ya clasificamos.

Listas en Python

Ahora que ya tenemos la habilidad de extraer texto crudo de las páginas web, lo que queremos es tener ese texto en una forma que sea fácil de procesar. Hasta aquí, cuando necesitábamos guardar información en nuestros programas en Python usábamos cadenas. Hubo un par de excepciones, de todos modos. En la rutina para quitar las etiquetas del HTML, también usamos un entero llamado inside que tomaba el valor 1 cuando procesábamos una etiqueta y 0 cuando no lo hacíamos (más información en la página de discusión del wiki).

inside = 1

Y, por otro lado, para leer o escribir en un archive nosotros usamos un manejador de archivo especial al que le pusimos f en el ejemplo de más arriba.

f = open('holamundo.txt','w')
f.write('hola mundo')
f.close()

De todos modos, uno de los tipos más usados de objetos que Python provee es la lista, una colección ordenada de otros objetos (incluidas, potencialmente, otras listas). El hecho de que las listas pueden contener listas hace que sean ideales para almacenar estructuras de tipo árbol, algo que explicaremos pronto y sobre lo que volveremos repetidamente. Es muy simple convertir una cadena a una lista de caracteres o una lista de palabras, como muestra el siguiente programa. Lo copiamos al Komodo, lo guardamos como cadena-a-lista.py y lo ejecutamos. Compare las dos listas que serán impresas en el panel de salida del Komodo.

# cadena-a-lista.py
 
# algunas cadenas
s1 = 'hola mundo'
s2 = 'Que tal mundo'
 
# lista de caracteres
charlist = []
for char in s1:
    charlist.append(char)
print charlist
 
# lista de 'palabras'
wordlist = s2.split()
print wordlist

La primera rutina usa un bucle for para tomar un caracter por vez de la cadena s1 y agregarlo appends al final de la lista llamada charlist. La segunda rutina hace uso del método split para quebrar la cadena s2 allí donde tiene espacios en blanco (espacios, tabulaciones, retorno de párrafo y caracteres similares). A decir verdad, es una simplificación llamar a los objetos de la segunda lista “palabras”. Trate cambiando s2 en el programa de arriba por ‘Qué tal mundo!’ y ejecútelo nuevamente. ¿Qué pasa con el signo de admiración?
Dado que aprendió tanto, usted puede ahora abrir una URL, bajar la página web a una cadena, quitarle las marcas HTML y luego dividir el texto en una lista de palabras. Pruebe ejecutando este programa:

# html-a-lista-1.py
 
import urllib2
import dh
 
url = 'http://niche.uwo.ca/programming-historian/dcb/dcb-34298.html'
 
response = urllib2.urlopen(url)
html = response.read()
texto = dh.stripTags(html)
wordlist = texto.split()
print wordlist[0:120]

Debería haber obtenido lo siguiente:

['Dictionary', 'of', 'Canadian', 'Biography', 'DOLLARD', 'DES', 
'ORMEAUX', '(called', 'Daulat', 'in', 'his', 'death', 'certificate', 
'and', 'Daulac', 'by', 'some', 'historians),', 'ADAM,', 'soldier,',
'\x93garrison', 'commander', 'of', 'the', 'fort', 'of', 
'Ville-Marie', '[Montreal]\x94;', 'b.', '1635,', 'killed', 'by', 
'the', 'Iroquois', 'at', 'the', 'Long', 'Sault', 'in', 
'May&nbsp;1660.', '\xa0\xa0\xa0\xa0\xa0', 'Nothing', 'is', 'known', 
'of', 'Dollard\x92s', 'activities', 'prior', 'to', 'his', 'arrival', 
'in', 'Canada', 'except', 'that', '\x93he', 'had', 'held', 'some', 
'commands', 'in', 'the', 'armies', 'of', 'France.\x94', 'Having', 
'come', 'to', 'Montreal', 'as', 'a', 'volunteer,', 'very', 
'probably', 'in', '1658,', 'he', 'continued', 'his', 'military', 
'career', 'there.', 'In', '1659', 'and', '1660', 'he', 'was', 
'described', 'as', 'an', '\x93officer\x94', 'or', '\x93garrison', 
'commander', 'of', 'the', 'fort', 'of', 'Ville-Marie,\x94', 'a', 
'title', 'that', 'he', 'shared', 'with', 'Pierre', 'Picot\xe9', 
'de', 'Belestre.', 'We', 'do', 'not', 'however', 'know', 'what', 
'his', 'particular', 'responsibility', 'was.']

Pero con tener una lista de palabras no lograremos mucho. Tendremos que hacer que nuestros programas puedan procesarla.

contratapas raras, contratapas malas


Trampa 22

Quería leer Trampa 22 (Catch-22) de Joseph Heller desde hace rato. Encontré un ejemplar, lo compré.
La novela es imperdible. Pero el ejemplar que compré (de la editorial RBA Libros, 2007) es también impar.
En la tapa y en el lomo dice que el libro es de Joseph Heller (bien), en el frontis la cosa cambia y Trampa 22 se le atribuye a Joseph Keller. Este Keller, según la wikipedia, es un matemático conocido por su teoría geométrica de la difracción.
En la contratapa el panorama se enrarece aún más. Allí se dice que el libro fue escrito por Val McDermid. Este sí es un escritor. Según su página web publicó recientemente The Fever of the Bone, y hasta donde leí, nunca publicó una novela que se llama Trampa 22 o fue teniente de las fuerzas aéreas estadounidenses en Córcega (como aparentemente sí fue Heller y/o Keller).
Se trata de dos o tres errores o dos o tres casos de suplantación de identidad. Es entendible.
Lo que no es para nada comprensible es el resumen de la novela en la contratapa.
Se me ocurrió que podría hacerse (¿o ya está hecho?) un cuaderno o un blog con las más increíbles (por peores o extravagantes) reseñas de contratapa jamás leídas. La de esta edición de Trampa 22 es parte de esa lista imaginaria, y lo es por mala.

quotep

La acción se desarrolla durante los últimos mese de la Segunda Guerra Mundial y se centra en una escuadrilla de bombarderos estadounidense. El coronel Cathcart, jefe de la cuadrilla, quiere ser ascendido a general. Y no encuentra mejor manera que enviar a sus hombres a realizar las misiones más peligrosas.
Con una lógica siniestra, Yossarian, un piloto subordinado de Cathcart que intenta ser eximido del servicio alegando enfermedad mental, recibe por respuesta que sólo los locos aceptan misiones aéreas y que su disgusto demuestra que está sano y, por tanto, es apto para volar. La evolución psicológica de Yossarian refleja la aguda crítica que hace Joseph Heller de un patriotismo mal entendido, que exige sacrificios inadmisibles.



En la página web de la editorial RBA, el asunto se arregla un poco con una oración que allí aparece pero en el libro no.
¿Quién conoce otros casos para la lista?

1936, la revolución española


The Ex, 1936



The Ex es un banda holandesa con muchos años encima (en su sitio se los ve patinando en el hielo, pero este disco es anarcopunk, en serio). Originalmente,1936, The Spanish Revolution fueron dos simples y un libro bilingüe (que no tengo) de 144 páginas, con fotos vistas y otras inéditas de los archivos de la CNT y la FAI, producido en conmemoración de los 30 años de la "revolución española". Después hubo versiones en CD, los temas se pueden escuchar en la red, varias fotos circulan, pero el libro no aparece fácilmente. Así que vamos viendo y escuchando estas versiones extrañas del tren blindado y ay carmela. Todo el paquete es una buena cosa.
Un googleo nos llevó hasta acá para ver algunas fotos y las letras de estos temas; hasta aquí para ver el diseño de los vinilos y más fotos; y hasta más allá para una buena cantidad de fotos. Hay un archivo bittorrent con los temas y las fotos (al parecer todas)…¿alguien leyó el libro?

Disco 1
They Shall Not Pass

El Tren Blindado

Disco 2
Ay Carmela

People Again


The Ex, tren blindado

coda: las fotos las bajé de aquí.

Images is enhanced with WordPress Lightbox 2 by Zeo