functional copy of the folder in Algolit repository -Algoliterary Publishing- where this project is still saved.

master
ana mertens 3 years ago
parent 14e27bec96
commit 7d6da2e2bd

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<comment version="3.0">
<caption/>
<note>User comments</note>
<place/>
<categories/>
</comment>

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<comment version="3.0">
<caption/>
<note>charset=&quot;Ascii&quot; User comments</note>
<place/>
<categories/>
</comment>

@ -0,0 +1,119 @@
abedul/ abedules (el) birch
abeto/-s (el) fir
abeto/-s blanco/-s (el) silver fir
acacia/-s (la) acacia
acebo/-s (el) holly tree
acebuche/-es (el) wild olive tree
aguacate/-es (el) avocado pear tree
álamo/-s (el) poplar
albaricoquero/-s (el) apricot tree
alcanforero /-s (el) camphor tree
alcornoque /-es (el) cork tree
algarrobo/-s (el) carob tree
aliso /-s (el) alder tree
almendro/-s (el) almond tree
arce/ -s (el) maple tree
arce/-s rojo/-s (el) red maple tree
baobab /-s (el) baobab
caoba/-s (la) mahogany
castaño/-s (el) chestnut tree
castaño/s de Indias (el) horse chestnut tree
cedro/-s (el) cedar
cerezo /-s (el) cherry tree
chopo /-s (el) black poplar
ciprés / cipreses (el) cypress
ciruelo /-s (el) plum tree
ciruelo/-s rojo/-s (el) red plum tree
cocotero /-s (el) coconut tree
ébano /-s (el) ebony
encina/-s (la) holm oak
eucalipto/-s (el) eucalyptus tree
fresno /-s (el) ash tree
granado/-s (el) pomegranate tree
guayacán/ guayacanes (el) lignum vitae
haya/-s (la) beech tree
higuera/-s (la) fig tree
lichi/-s (el) lychee
limonero/-s (el) lemon tree
mango/-s (el) mango tree
manzano /-s (el) apple tree
melocotonero/-s (el) peach tree
morera/-s (la) mulberry tree
naranjo/-s (el) orange tree
nogal/ nogales (el) walnut tree
olivo/-s (el) olive tree
olmo/-s (el) elm
ombú / ombúes (el) ombu tree
palmera/-s (la) palmtree, palm
pino/-s (el) pine
pino/s piñonero/s (el) stone pine
plátano/s de sombra (el) plane tree
roble/-s (el) oak
sabina albar / sabinas albares (la) savine Juniper
sauce/ sauces (el) willow
sauce llorón / sauces llorones (el) weeping willow
secuoya/-s (la) sequoia, redwood tree
sicomoro/-s (el) sycamore
teca /-s (la) teak tree
tejo /-s (el) yew tree
tilo /-s (el) lime tree
velintonia /-s (la) wellingtonia tree

Binary file not shown.

161
app.py

@ -0,0 +1,161 @@
#!/usr/bin/env/ python
# This is the webinterface for 'la_distancia_de_levenshtein_lee_a_cortazar'
# and generates the pdf using weasyprint.
# Copyright (C) 2021, Anais Berck
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details: <http://www.gnu.org/licenses/>.
import textwrap
from io import StringIO
from os.path import dirname
import re
import datetime
import os
from fcntl import lockf, LOCK_EX, LOCK_UN
from flask import Flask, Response, render_template, request, send_file
from weasyprint import HTML
from la_distancia_de_levenshtein_lee_a_cortazar import (
calculate_distances, find_nearest_species, generate_in_between_species,
generate_new_fragment, openfile, print_map, print_table, sort_distances)
BASEURL = '/levenshtein'
COUNTER_PATH = 'edition_counter.txt'
def strip_but_spaces_and_words (text ):
return re.sub(r'[^\w\s\d]', '', text)
def wrap (text, width):
return'\n'.join(['\n'.join(textwrap.wrap(line, width=width)) for line in text.splitlines()])
def read_sources (*paths):
return [ (p, wrap(open(p, 'r').read(), 120)) for p in paths ]
def get_edition_count():
fd = os.open(COUNTER_PATH, os.O_RDWR|os.O_CREAT)
lockf(fd, LOCK_EX)
fo = os.fdopen(fd, 'r+', encoding='utf-8')
content = fo.read()
if not content:
edition_count = 0
else:
edition_count = int(content.strip())
edition_count += 1
fo.seek(0)
fo.truncate()
fo.write(str(edition_count))
fo.flush()
lockf(fd, LOCK_UN)
os.close(fd)
return edition_count
app = Flask(__name__)
trees = []
## 1A. Open textfiles & turn textfiles in machine readable lists
# Cortazar
txt = 'eucalipto_cortazar.txt'
all_plural = openfile('arboles_plural.txt')
all_simple = openfile('arboles_simple.txt')
## 1B. Turn list of trees into dictionary of single/plural words
trees = dict(zip(all_simple, all_plural))
## 2. HOMEPAGE
## laod fragment Cortazar
with open(txt, 'r') as source:
fragment = source.read()
# cover_distances = sort_distances(calculate_distances('eucalipto', all_simple))
fragment_words = re.split(r'[\n\s]+', strip_but_spaces_and_words(fragment).lower())
fragment_word_distances = sort_distances(calculate_distances('eucalipto', fragment_words))
@app.route('/')
def index():
return render_template('index.html', trees=trees, BASEURL=BASEURL)
@app.route('{}/generate'.format(BASEURL), methods=['POST'])
def generate():
edition_count = get_edition_count()
new_main_tree = str(request.form['selected_tree'])
if new_main_tree in all_simple:
# Generate map for the cover
# cover_map = StringIO()
# print_map(cover_distances, show_distances=False, file_out=cover_map)
fragment_cover_map = StringIO()
print_map(fragment_word_distances, show_distances=False, file_out=fragment_cover_map)
## 3. Count the similarity between the main tree and the other species in the forest
similarities = calculate_distances(new_main_tree, all_simple)
## 4. Sort the similarities between the trees and the new main tree from the lowest to the highest counts
sorted_similarities = sort_distances(similarities)
# Find the nearest species
near_species, nearest_species = find_nearest_species(sorted_similarities, trees)
new_fragment = generate_new_fragment(fragment, new_main_tree, nearest_species)
## 5. Compare the similarity between the main character tree and the main species in the forest,
repetitive_poetry = StringIO()
in_between_species = generate_in_between_species(new_main_tree, near_species, file_out=repetitive_poetry)
forest_map = StringIO()
print_map(sorted_similarities, file_out=forest_map)
table_of_intermediary_species = StringIO()
print_table(new_main_tree, near_species, in_between_species, table_of_intermediary_species)
now = datetime.datetime.now()
context = {
'edition_count': edition_count,
# 'cover_map': cover_map.getvalue(),
'date': now.strftime('%d-%m-%Y'),
'time': now.strftime('%H:%M:%S'),
'fragment_cover_map': fragment_cover_map.getvalue(),
'forest_map': forest_map.getvalue(),
'new_fragment': wrap(new_fragment, 85),
'repetitive_poetry': wrap(repetitive_poetry.getvalue(), 55),
'table_of_intermediary_species': table_of_intermediary_species.getvalue(),
'sources': read_sources('la_distancia_de_levenshtein_lee_a_cortazar.py'),
'BASEDIR': dirname(__file__)
}
raw_html = render_template('print.html', **context)
pdf = HTML(string=raw_html).write_pdf()
repetitive_poetry.close()
forest_map.close()
table_of_intermediary_species.close()
# return send_file(pdf, attachment_filename='La distancia de Levenshtein {}.pdf'.format(edition_count), as_attachment=True)
r = Response(pdf, mimetype='application/pdf')
r.headers.extend({
'Content-Disposition': 'attachment; filename="La distancia de Levenshtein {}.pdf"'.format(edition_count)
})
return r
return '500'

@ -0,0 +1,90 @@
abedules
abetos
acebos
acebuches
aguacates
ailantos
aladiernos
álamos
albaricoqueros
alcanforeros
alcornoques
algarrobos
alisos
almendros
almeces
arces
arraclánes
avellanos
baobabs
bojs
boneteros
brezos
carballos
carpes
castaños
cedros
cerezos
chopos
cinamomos
cipréses
ciruelos
cocoteros
cornejos
ébanos
enebros
espantaloboses
espinos
eucaliptos
evónimos
fresnos
galaperos
granados
guayacanes
guindos
labiérnagos
laureles
lentiscos
lichis
limoneros
loros
madroños
maguillos
majuelos
mangos
manzanos
melocotoneros
melojos
mostellares
naranjos
negundos
nogales
olivos
olmos
ombúes
palmitos
perales
pereteros
pimenteros
pinabetes
pinos
pinsapos
piruétanos
plátanos
quejigos
rebollos
robles
sanguinos
sargatillos
sauces
saúcos
serbales
sicomoros
tabacos
tamariscos
tarajes
tarayes
tejos
temblónes
terebintos
tilos

@ -0,0 +1,90 @@
abedul
abeto
acebo
acebuche
aguacate
ailanto
aladierno
álamo
albaricoquero
alcanforero
alcornoque
algarrobo
aliso
almendro
almez
arce
arraclán
avellano
baobab
boj
bonetero
brezo
carballo
carpe
castaño
cedro
cerezo
chopo
cinamomo
ciprés
ciruelo
cocotero
cornejo
ébano
enebro
espantalobos
espino
eucalipto
evónimo
fresno
galapero
granado
guayacán
guindo
labiérnago
laurel
lentisco
lichi
limonero
loro
madroño
maguillo
majuelo
mango
manzano
melocotonero
melojo
mostellar
naranjo
negundo
nogal
olivo
olmo
ombú
palmito
peral
peretero
pimentero
pinabete
pino
pinsapo
piruétano
plátano
quejigo
rebollo
roble
sanguino
sargatillo
sauce
saúco
serbal
sicomoro
tabaco
tamarisco
taraje
taray
tejo
temblón
terebinto
tilo

@ -0,0 +1,23 @@
acacia
bardaguera
caoba
carrasquilla
cornicabra
coscoja
encina
higuera
mimbre
mimbrera
mimosa
morera
palma
palmera
robinia
sabina
salciña
sarga
secuoya
sófora
teca
velintonia
haya (m)

@ -0,0 +1,69 @@
import math
import random
words = [
(8, ' AAA '),
(6, ' BBB '),
(8, ' CCC '),
(8, ' DDD '),
(3, ' EEE '),
(8, ' FFF '),
(2, ' GGG '),
]
height = 30
width = 40
middle = (20, 15)
grid = [[] for x in range(height + 1)]
grid[middle[1]].append((middle[0] * 2, (middle[0] * 2 + 1), 'X'))
def space_available (grid, start, end, line):
other_words = grid[line]
for other_start, other_end, _ in other_words:
if start < other_end and end > other_start:
return False
return True
for distance, word in words:
placed = False
angle = random.random() * math.pi * 2
step = 0
steps = 20
while (not placed) and step < steps:
x = int(round(math.cos(angle) * distance) + middle[0]) * 2
y = int(round(math.sin(angle) * distance) + middle[1])
start = x - max(0, int(math.floor(len(word) / 2)))
end = start + len(word)
if space_available(grid, start, end, y):
grid[y].append((start, end, word))
placed = True
angle += (math.pi * 2 / steps)
# print(angle, x, y)
print(grid)
for row in grid:
# Sort by first key of the tuples, start of the word
row = sorted(row, key=lambda r: r[0])
if not len(row):
print (width * ' ')
else:
line = ''
for start, _, word in row:
line += (start - len(line)) * ' ' + word
line += (width - len(line)) * ' '
print(line)

@ -0,0 +1,12 @@
Un fama anda por el bosque y aunque no necesita leña mira codiciosamente los árboles. Los árboles tienen un miedo terrible porque conocen las costumbres de los famas y temen lo peor. En medio de todos está un eucalipto hermoso, y el fama al verlo da un grito de alegría y baila tregua y baila catala en torno del perturbado eucalipto, diciendo así:
Hojas antisépticas, invierno con salud, gran higiene.
Saca un hacha y golpea al eucalipto en el estómago, sin importársele nada. El eucalipto gime, herido de muerte, y los otros árboles oyen que dice entre suspiros:
Pensar que este imbécil no tenía más que comprarse unas pastillas Valda.

@ -0,0 +1,242 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tabakalera</title>
<style>
html, body {
font-size: 9pt;
line-height: 11pt;
font-family: "Fira Mono";
margin: 0;
padding: 0;
}
:root {
--running-header: "Levenshtein distance"
}
@page {
size: A4;
margin: 20mm 20mm 30mm 20mm;
}
@page:left {
@bottom-left {
content: counter(page);
}
@top-left {
content: var(--running-header);
}
}
@page:right {
@bottom-right {
content: counter(page);
}
@top-right {
content: var(--running-header);
}
}
</style>
</head>
<body>
<p>
dictionary with sorted similarities: [('catala', 0), ('baila', 3), ('fama', 4), ('nada.', 4), ('famas', 4),
('hacha', 4), ('al', 4), ('anda', 4), ('saca', 4), ('da', 5), ('otros', 5), ('salud', 5), ('entre', 5), ('así:', 5),
('golpea', 5), ('mira', 5), ('del', 5), ('está', 5), ('lo', 5), ('valda.', 5), ('tregua', 5), ('unas', 5), ('el',
5), ('las', 5), ('tenía', 5), ('pastillas', 5), ('con', 5), ('verlo', 5), ('hojas', 5), ('gran', 5), ('este', 5),
('leña', 5), ('grito', 6), ('', 6), ('medio', 6), ('por', 6), ('muerte', 6), ('más', 6), ('en', 6), ('torno', 6),
('de', 6), ('gime', 6), ('pensar', 6), ('herido', 6), ('temen', 6), ('todos', 6), ('árboles', 6), ('bosque', 6),
('oyen', 6), ('aunque', 6), ('dice', 6), ('sin', 6), ('tienen', 6), ('que', 6), ('peor.', 6), ('conocen', 6),
('miedo', 6), ('porque', 6), ('estómago', 6), ('un', 6), ('los', 6), ('necesita', 6), ('y', 6), ('alegría', 6),
('no', 6), ('comprarse', 7), ('diciendo', 7), ('árboles.', 7), ('hermoso', 7), ('eucalipto', 7), ('imbécil', 7),
('terrible', 7), ('costumbres', 8), ('perturbado', 8), ('invierno', 8), ('higiene.', 8), ('suspiros:', 9),
('antisépticas', 10), ('importársele', 10), ('codiciosamente', 12)]
</p>
<p>
This text will be slowly rewritten by similar words.<br>
You can choose the degree of similarity for its rewriting by typing a number between 1 and 10 :<br>
5<br>
possible words with similarity degree of 5 : ['da', 'otros', 'salud', 'entre', 'así:', 'golpea', 'mira', 'del',
'está', 'lo', 'valda.', 'tregua', 'unas', 'el', 'las', 'tenía', 'pastillas', 'con', 'verlo', 'hojas', 'gran',
'este', 'leña']<br>
word to be replaced in text: catala<br>
replacement word: unas
</p>
<p>
element of original word in text to be compared: c<br>
element of replacement word to be compared: u<br>
in between word where selected letters of original word replace selected letters of replacement word: cnas<br>
difference between c and u : 0
</p>
c → u
[c → u]nas
c <> u 1
<p>
element of original word in text to be compared: c<br>
element of replacement word to be compared: un<br>
in between word where selected letters of original word replace selected letters of replacement word: cas<br>
difference between c and un : 1
</p>
<p>
element of original word in text to be compared: c<br>
element of replacement word to be compared: una<br>
in between word where selected letters of original word replace selected letters of replacement word: cs<br>
difference between c and una : 2
</p>
<p>
element of original word in text to be compared: c<br>
element of replacement word to be compared: unas<br>
in between word where selected letters of original word replace selected letters of replacement word: c<br>
difference between c and unas : 3
</p>
<p>
element of original word in text to be compared: ca<br>
element of replacement word to be compared: u<br>
in between word where selected letters of original word replace selected letters of replacement word: canas<br>
difference between ca and u : 1
</p>
<p>
element of original word in text to be compared: ca<br>
element of replacement word to be compared: un<br>
in between word where selected letters of original word replace selected letters of replacement word: caas<br>
difference between ca and un : 1
</p>
<p>
element of original word in text to be compared: ca<br>
element of replacement word to be compared: una<br>
in between word where selected letters of original word replace selected letters of replacement word: cas<br>
difference between ca and una : 2
</p>
<p>
element of original word in text to be compared: ca<br>
element of replacement word to be compared: unas<br>
in between word where selected letters of original word replace selected letters of replacement word: ca<br>
difference between ca and unas : 3
</p>
<p>
element of original word in text to be compared: cat<br>
element of replacement word to be compared: u<br>
in between word where selected letters of original word replace selected letters of replacement word: catnas<br>
difference between cat and u : 2
</p>
<p>
element of original word in text to be compared: cat<br>
element of replacement word to be compared: un<br>
in between word where selected letters of original word replace selected letters of replacement word: catas<br>
difference between cat and un : 2
</p>
<p>
element of original word in text to be compared: cat<br>
element of replacement word to be compared: una<br>
in between word where selected letters of original word replace selected letters of replacement word: cats<br>
difference between cat and una : 2
</p>
<p>
element of original word in text to be compared: cat<br>
element of replacement word to be compared: unas<br>
in between word where selected letters of original word replace selected letters of replacement word: cat<br>
difference between cat and unas : 2
</p>
<p>
element of original word in text to be compared: cata<br>
element of replacement word to be compared: u<br>
in between word where selected letters of original word replace selected letters of replacement word: catanas<br>
difference between cata and u : 3
</p>
<p>
element of original word in text to be compared: cata<br>
element of replacement word to be compared: un<br>
in between word where selected letters of original word replace selected letters of replacement word: cataas<br>
difference between cata and un : 3
</p>
<p>
element of original word in text to be compared: cata<br>
element of replacement word to be compared: una<br>
in between word where selected letters of original word replace selected letters of replacement word: catas<br>
difference between cata and una : 3
</p>
<p>
element of original word in text to be compared: cata<br>
element of replacement word to be compared: unas<br>
in between word where selected letters of original word replace selected letters of replacement word: cata<br>
difference between cata and unas : 3
</p>
<p>
element of original word in text to be compared: catal<br>
element of replacement word to be compared: u<br>
in between word where selected letters of original word replace selected letters of replacement word: catalnas<br>
difference between catal and u : 4
</p>
<p>
element of original word in text to be compared: catal<br>
element of replacement word to be compared: un<br>
in between word where selected letters of original word replace selected letters of replacement word: catalas<br>
difference between catal and un : 4
</p>
<p>
element of original word in text to be compared: catal<br>
element of replacement word to be compared: una<br>
in between word where selected letters of original word replace selected letters of replacement word: catals<br>
difference between catal and una : 4
</p>
<p>
element of original word in text to be compared: catal<br>
element of replacement word to be compared: unas<br>
in between word where selected letters of original word replace selected letters of replacement word: catal<br>
difference between catal and unas : 3
</p>
<p>
element of original word in text to be compared: catala<br>
element of replacement word to be compared: u<br>
in between word where selected letters of original word replace selected letters of replacement word: catalanas<br>
difference between catala and u : 5
</p>
<p>
element of original word in text to be compared: catala<br>
element of replacement word to be compared: un<br>
in between word where selected letters of original word replace selected letters of replacement word: catalaas<br>
difference between catala and un : 5
</p>
<p>
element of original word in text to be compared: catala<br>
element of replacement word to be compared: una<br>
in between word where selected letters of original word replace selected letters of replacement word: catalas<br>
difference between catala and una : 5
</p>
<p>
element of original word in text to be compared: catala<br>
element of replacement word to be compared: unas<br>
in between word where selected letters of original word replace selected letters of replacement word: catala<br>
difference between catala and unas : 4
</p>
<p>
in between words: ['cnas', 'cas', 'cs', 'c', 'canas', 'caas', 'cas', 'ca', 'catnas', 'catas', 'cats', 'cat',
'catanas', 'cataas', 'catas', 'cata', 'catalnas', 'catalas', 'catals', 'catal', 'catalanas', 'catalaas', 'catalas',
'catala']
</p>
<p>
Un fama anda por el bosque y aunque no necesita leña mira codiciosamente los árboles. Los árboles tienen un miedo
terrible porque conocen las costumbres de los famas y temen lo peor. En medio de todos está un eucalipto hermoso, y
el fama al verlo da un grito de alegría y baila tregua y baila <<unas>> en torno del perturbado eucalipto, diciendo
así:
</p>
<p>
Hojas antisépticas, invierno con salud, gran higiene.
</p>
<p>
Saca un hacha y golpea al eucalipto en el estómago, sin importársele nada. El eucalipto gime, herido de muerte, y
los otros árboles oyen que dice entre suspiros:
</p>
<p>
Pensar que este imbécil no tenía más que comprarse unas pastillas Valda.
</p>
</body>
</html>

@ -0,0 +1,4 @@
from weasyprint import HTML
HTML(filename='content.html')\
.write_pdf('weasyprint-library.pdf')

@ -0,0 +1,315 @@
#!/usr/bin/env/ python
# This script unfolds the Levenhstein Distance algorithm,
# often used in spellcheckers and to detect similarity between texts.
# The algorithm reads a fragment on a eucalyptus tree from
# Julio Cortazar's 'Historias de Cronopios y Famas', Alfaguara, 2012.
# Copyright (C) 2021, Anais Berck
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details: <http://www.gnu.org/licenses/>.
# Python libraries
import random
import math
import random
import textwrap
import Levenshtein
import sys
# Functions
# Open & prepare textfile for machinal reading
def openfile(txt):
all_text = []
with open(txt, 'r') as source:
# Read the text
text = source.read()
# Remove punctuation
characters = ['\n', '-', ':', '.', ',']
for c in characters:
if c in text:
clean_text = text.replace(c,' ')
# Transform fragment in a list of words
words = clean_text.split()
# Recolt all unique words of the fragment in a set of words
for word in words:
word = word.lower()
word = word.strip()
all_text.append(word)
return all_text
# Levenhstein Distance
def levenshtein(a, b):
if not a: return len(b)
if not b: return len(a)
return min(levenshtein(a[1:], b[1:])+(a[0] != b[0]),
levenshtein(a[1:], b)+1,
levenshtein(a, b[1:])+1)
# Create map
def space_available (grid, start, end, line):
other_words = grid[line]
for other_start, other_end, _ in other_words:
if start < other_end and end > other_start:
return False
return True
# Create frame
def formatTable(words, cellWidth, cellHeight, padding=1, cells=6):
def makeRow (height):
return [''] * cellHeight
def makeLine (width, text = '', fillChar=' ', padding=1):
text = (padding * fillChar) + text
return text.center(width - padding, ' ') + (padding * fillChar)
out = ""
row = makeRow(cellHeight)
cell = 0
for word in words:
wrapped = textwrap.wrap(word, width=cellWidth - (2 * padding))
lines = padding * [makeLine(cellWidth, padding=padding)]
for line in wrapped:
lines.append(makeLine(cellWidth, text=line))
for _ in range(cellHeight - len(lines)):
lines.append(makeLine(cellWidth, padding=padding))
cell += 1
for (i, line) in enumerate(lines):
row[i] += line
if cell == cells:
out += '\n'.join(row)
row = makeRow(cellHeight)
cell = 0
if cell > 0:
out += '\n'.join(row)
row = makeRow(cellHeight)
return out
# print statements for interaction with reader
def pick_new_main_tree_terminal(fragment, all_simple, file_out=sys.stdout):
print(fragment, file=file_out)
## ask input reader
print("\nPara que La Distancia de Levenshtein pueda leer el texto y producir un libro único, necesitas cambiar una palabra.", file=file_out)
print("\nPuedes elegir otra especie de árbol para el eucalipto, el árbol principal del fragmento. La Distancia de Levenshtein calculará entonces qué especie se encuentra en su cercanía y se reemplazará en el fragmento la palabra más genérica 'árboles' por esta nueva especie.", file=file_out)
print("\nPor favor, elige el árbol principal de la lista siguiente:", file=file_out)
print(', '.join(all_simple), file=file_out)
print("\nQuiero reemplazar el eucalipto por:", file=file_out)
new_main_tree = input()
while new_main_tree not in all_simple:
print("Tal vez escribiste mal la especie, por favor intente de nuevo.", file=file_out)
new_main_tree = input()
return new_main_tree
# Levenshtein Distance between new main tree and other tree species
def calculate_distances (center_tree, other_trees):
# Declare dictionary object
distances = {}
# For every species in the list, compare the main tree to the selected species and count the similarity between both
for tree in other_trees:
# using the Levenhstein Distance algorithm
# count = levenshtein(new_main_tree,tree)
count = Levenshtein.distance(center_tree, tree)
# save each compared species and its similarity count the dictionary
distances[tree] = count
return distances
def sort_distances (distances):
return sorted(distances.items(), key = lambda kv: kv[1])
# Find the minimum distance between new main tree and near species
def find_nearest_species(sorted_distances, trees):
# First entry in sorted_distances is the new_main_tree, distance 0
# pick the next value
minimum_distance = sorted_distances[1][1]
possible_trees = []
for tree in sorted_distances[1:]:
if tree[1] == minimum_distance:
possible_trees.append(tree)
else:
break
near_species = random.choice(possible_trees)[0]
nearest_species = trees[near_species]
return (near_species, nearest_species)
# rewrite fragment Cortazar
def generate_new_fragment(fragment, new_main_tree, nearest_species):
new_fragment = fragment.replace("eucalipto", new_main_tree)
new_fragment = new_fragment.replace("árboles", nearest_species)
new_fragment = new_fragment.replace("Un fama anda", "Un fama ignorante anda")
return new_fragment
# generate in between species and show the process of the Levenhstein Distance algorithm
def generate_in_between_species (new_main_tree, near_species, file_out=sys.stdout):
# Define length of main character tree and major species
length_main_character_tree = len(new_main_tree)
length_near_species = len(near_species)
# Declare a list of in between words showing the process
in_between_species = []
# Loop over every number until reaching the total lenght of the original word
for cell in range(length_main_character_tree):
#print('row number: ', element)
# Loop over every number until reaching the total lenght of the replacement word
for number in range(length_near_species):
#print('column number: ', number)
# select the number of letters +1 of the original word
part1 = new_main_tree[:cell+1]
print('La Distancia de Levenshtein observa una parte del',new_main_tree, ':', part1, file=file_out)
# select the number of letters +1 of the replacement word
part2 = near_species[:number+1]
print('Después observa una parte del', near_species, ':', part2, file=file_out)
# selected letters of the original words replace the selected letters of the replacement word
new_species = part1 + near_species[number+1:]
print('En su intento de comparación reemplaza la parte del', near_species, 'por la parte del', new_main_tree, 'y crea así una nueva especie intermediaria: el', new_species, file=file_out)
# add in between words to the list
in_between_species.append(new_species)
# calculate the similarity between in between words
print('Calcula las acciones necesarias para reemplazar', new_main_tree[:cell+1], 'por', near_species[:number+1], ': ', Levenshtein.distance(new_main_tree[:cell+1], near_species[:number+1]), '\n', file=file_out)
# print('\n', file=file_out)
## Print all in between words
#print('in between species: ', in_between_species, file=file_out)
return in_between_species
# Draw a map of all in between species and their distance to main tree
def print_map(sorted_distances, show_distances=True, file_out=sys.stdout):
# As characters are less wide than high make them a bit wider
xscale = 2
# Height of the map
height = 70
# Width of the map
width = int(70 * xscale)
# Centerpoint of the map
middle = (int(30 * xscale), 35)
grid = [[] for x in range(height + 1)]
centerpoint = sorted_distances[0][0]
start = middle[0] - max(0, int(len(centerpoint) / 2))
grid[middle[1]].append((start, start + len(centerpoint), centerpoint))
for treename, distance in sorted_distances[1:]:
placed = False
treename = ' {}({}) '.format(treename, distance) if show_distances else ' {} '.format(treename)
angle = random.random() * math.pi * 2
step = 0
steps = 180
while (not placed) and step < steps:
x = int(math.floor(math.cos(angle) * (distance * 2.8)) * xscale + middle[0])
y = int(math.floor(math.sin(angle) * (distance * 2.8)) + middle[1])
start = x - max(0, int(len(treename) / 2))
end = start + len(treename)
if space_available(grid, start, end, y):
grid[y].append((start, end, treename))
placed = True
angle += (math.pi * 2 / steps)
step += 1
if not placed:
print('Could not place {}'.format(treename), file=file_out)
# print(angle, x, y)
for row in grid:
# Sort by first key of the tuples, start of the word
row = sorted(row, key=lambda r: r[0])
if len(row):
line = ''
for start, _, treename in row:
line += ((start) - len(line)) * ' ' + treename
print(line, file=file_out)
# draw table with all new intermediary species
def print_table(new_main_tree, near_species, in_between_species, file_out=sys.stdout):
## 8. Print tabel
print('{}{}'.format(new_main_tree, near_species), file=file_out)
print('', file=file_out)
print(formatTable(in_between_species, 20, 5, cells=len(near_species)), file=file_out)
# Execute functions
if __name__ == '__main__':
## 1A. Open textfiles & turn textfiles in machine readable lists
# Cortazar
txt = 'eucalipto_cortazar.txt'
# Open textfile
all_text = openfile(txt)
# List of trees
txt1 = 'arboles_simple.txt'
txt2 = 'arboles_plural.txt'
all_simple = openfile(txt1)
all_plural = openfile(txt2)
## 1B. Turn list of trees into dictionary of single/plural words
trees = dict(zip(all_simple, all_plural))
## 2. HOMEPAGE print statements
## print fragment Cortazar
with open(txt, 'r') as source:
fragment = source.read()
## 2b. Ask user to pick a new main tree
new_main_tree = pick_new_main_tree_terminal(fragment, all_simple, sys.stdout)
## 3. Count the similarity between the main tree and the other species in the forest
distances = calculate_distances(new_main_tree, all_simple)
## 4. Sort the distances between the trees and the new main tree from the lowest to the highest counts
sorted_distances = sort_distances(distances)
# Find the nearest species
near_species, nearest_species = find_nearest_species(sorted_distances, trees)
# Print rewritten fragment
print("\n")
new_fragment = generate_new_fragment(fragment, new_main_tree, nearest_species)
print(new_fragment, file=sys.stdout)
## 6. Compare the similarity between the main character tree and the main species in the forest,
in_between_species = generate_in_between_species(new_main_tree, near_species, file_out=sys.stdout)
# Show the sorted distances dictionary
# print('Sorted distances: ', sorted_distances, file=sys.stdout)
# print('\n', file=sys.stdout)
# generate_new_text(fragment, new_main_tree, all_simple, all_plural, trees, sys.stdout)
## 7. Generate map of the woods
print_map(sorted_distances, file_out=sys.stdout)
## 8. Generate intermediary species table
print_table(new_main_tree, near_species, in_between_species, sys.stdout)

@ -0,0 +1,3 @@
WeasyPrint
python-Levenshtein
flask

@ -0,0 +1,48 @@
#!/usr/bin/env/ python
# encoding=utf8
from bs4 import BeautifulSoup
import requests
species = []
name_species = ''
url = "https://www.arbolapp.es/especies-nombre-cientifico/"
# Getting the webpage, creating a Response object.
response = requests.get(url)
if response:
# Extracting the source code of the page.
data = response.text
soup = BeautifulSoup(data, 'lxml')
# find all elements inside a div element of class contenido
selector = 'div.contenido > h4'
# find elements that contain the data we want
found = soup.select(selector)
for element in found:
heading_data = element.text
print(heading_data.lower())
# print('soup:', soup)
# for link in soup.find_all("a"):
# url = link.get("href", "")
# print('url:', url)
# if "/wiki/" in url:
# name_species = url.replace("/wiki/", "")
# species.append(name_species)
# destination = "List_of_tree_genera.txt"
# with open(destination, 'w') as source:
# for specie in species:
# source.write(specie)
# source.write('\n')
else:
pass
# complete_links =["https://en.wikipedia.org/wiki/", "https://es.wikipedia.org/wiki/", "https://fr.wikipedia.org/wiki/", "https://nl.wikipedia.org/wiki/"]
'''
comments:
Trees of Africa refer to all countries listed here: https://en.wikipedia.org/wiki/Ecoregions_of_Africa
'''

@ -0,0 +1,93 @@
Copyright 2020 The XanhMono Project Authors (https://github.com/yellow-type-foundry/xanhmono).
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -0,0 +1,359 @@
<!DOCTYPE html>
<html lang="en">
<head>
<style>
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
</style>
<style>
@font-face {
font-family: XanhMono;
src: url(static/fonts/XanhMono-Regular.woff2) format('woff2'),
url(static/fonts/XanhMono-Regular.woff) format('woff'),
url(static/fonts/XanhMono-Regular.ttf) format('truetype');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: XanhMono;
src: url(static/fonts/XanhMono-Italic.woff2) format('woff2'),
url(static/fonts/XanhMono-Italic.woff) format('woff'),
url(static/fonts/XanhMono-Italic.ttf) format('truetype');
font-weight: 400;
font-style: italic;
}
:root {
--font-size: 13pt;
--line-height: 17pt;
--font-size-smaller: 11pt;
--line-height--smaller: 15pt;
}
body {
font-family: XanhMono;
margin: 0;
font-size: var(--font-size);
line-height: var(--line-height);
display: flex;
flex-direction: row;
overflow: hidden;
max-height: 100vh;
}
h1 {
font-size: 24pt;
line-height: 29pt;
}
h2 {
font-size: 20pt;
line-height: 29pt;
}
a {
color: currentColor;
}
a:hover {
text-decoration: none;
}
blockquote {
font-style: italic;
}
p, form {
margin-top: var(--line-height);
}
footer {
font-style: normal;
margin-top: var(--line-height);
font-size: var(--font-size-smaller);
line-height: var(--line-height--smaller);
}
input, select, option, button {
font: inherit;
}
.panel {
padding: calc(2 * var(--line-height)) calc(2 * var(--line-height)) calc(2 * var(--line-height)) var(--line-height);
overflow: auto;
max-height: 100%;
}
.panel--original-fragment,
.panel--new-fragment {
flex: 1.5;
}
.panel--about {
flex: 1;
font-size: var(--font-size-smaller);
line-height: var(--line-height--smaller);
background: rgb(220, 236, 220);
min-height: calc(100vh - 4 * var(--line-height));
}
label {
margin-top: calc(0.25 * var(--line-height));
vertical-align: middle;
display: inline-block;
margin-right: 0.25em;
}
input[type="radio"] {
margin: 0 .25em 0 0;
vertical-align: middle;
}
button {
display: block;
margin-top: var(--line-height);
margin-bottom: var(--line-height);
}
#language_switcher {
position: fixed;
top: var(--line-height);
right: var(--line-height);
}
.panel--about[lang="es"] [lang="en"],
.panel--about[lang="en"] [lang="es"] {
display: none;
}
.message {
display: none;
font-size: var(--font-size-smaller);
line-height: var(--line-height--smaller);
font-style: italic;
margin: var(--line-height--smaller) 0;
}
.message[data-active] {
display: block;
}
@media screen and (max-width: 900px) {
:root {
--font-size: 11.5pt;
--line-height: 14.5pt;
--font-size-smaller: 10pt;
--line-height--smaller: 13pt;
}
h1 {
font-size: 20pt;
line-height: 25pt;
}
h2 {
font-size: 16pt;
line-height: 20pt;
}
body {
flex-direction: column;
overflow: initial;
}
.panel {
max-height: initial;
min-height: initial;
height: initial;
flex: 0 0 auto;
overflow: initial;
}
.panel--about {
order: -1;
}
}
</style>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>La Distancia de Levenshtein lee a Cortázar</title>
</head>
<body>
<section class="panel panel--original-fragment">
<h2>Fama y eucalipto</h2>
<blockquote>
<p>
Un fama anda por el bosque y aunque no necesita leña mira codiciosamente los árboles. Los árboles tienen un miedo terrible porque conocen las costumbres de los famas y temen lo peor. En medio de todos está un eucalipto hermoso, y el fama al verlo da un grito de alegría y baila tregua y baila catala en torno del perturbado eucalipto, diciendo así:
</p>
<p>
Hojas antisépticas, invierno con salud, gran higiene.
</p>
<p>
Saca un hacha y golpea al eucalipto en el estómago, sin importársele nada. El eucalipto gime, herido de muerte, y los otros árboles oyen que dice entre suspiros:
</p>
<p>
Pensar que este imbécil no tenía más que comprarse unas pastillas Valda.
</p>
<footer>Historias de Cronopios y Famas de Julio&nbsp;Cortázar, publicada en 1962 por la Editorial&nbsp;Minotauro</footer>
</blockquote>
</section>
<section class="panel panel--new-fragment">
<h2>A ti</h2>
<p>
Para que La Distancia de Levenshtein pueda leer el fragmento y producir un libro único, necesitas cambiar una palabra.
</p>
<p>
Puedes elegir otra especie de árbol para el eucalipto, el árbol principal del fragmento. La Distancia de Levenshtein calculará entonces qué especie se encuentra en su cercanía y se reemplazará en el fragmento la palabra más genérica 'árboles' por esta nueva especie.
</p>
<form action="{{ BASEURL }}/generate" method="POST">
<!-- <select name="selected_tree"> -->
{% for tree in trees %}
<label><input type="radio" name="selected_tree" value="{{ tree }}" />{{ tree }}</label>
{% endfor %}
<!-- </select> -->
<div class="message" data-message="working">La Distancia de Levenshtein está escribiendo, por favor espere un momento.</div>
<div class="message" data-message="error">Ocurrió un error, inténtalo de nuevo por favor.</div>
<button type="submit">Generar</button>
</form>
</section>
<section class="panel panel--about" lang="es">
<nav id="language_switcher">
<a href="#" data-target-lang="es">ES</a>
<a href="#" data-target-lang="en">EN</a>
</nav>
<section lang="es">
<h1>La Distancia de Levenshtein lee a Cortázar</h1>
<p>'La Distancia de Levenshtein lee a Cortázar' es el quinto capítulo de <a href = "https://www.tabakalera.eus/es/agora-cemento-codigo">ÁGORA / CEMENTO / CÓDIGO</a>, una exposición online comisariada por <a href ="https://www.tabakalera.eus/es/lekutan">Lekutan</a>, dentro del programa Komisario Berriak, proyecto apoyado por <a href = "https://www.tabakalera.eus">Tabakalera</a> en Donostia / San Sebastián. <a href = "https://www.anaisberck.be">Anaïs Berck</a> presenta aquí una primera versión de un libro en la editorial 'Editorial Algoliteraria: crear alianzas con los árboles'. En esta editorial los autores son algoritmos y los libros presentan los resultados narrativos escritos desde su punto de vista.</p>
<p>El autor de este libro es el algoritmo <a href ="https://es.wikipedia.org/wiki/Distancia_de_Levenshtein">La Distancia de Levenhstein</a>, el tema es el eucalipto en "Fama y eucalipto", un fragmento de <a href ="https://es.wikipedia.org/wiki/Historias_de_cronopios_y_de_famas">Historias de Cronopios y de Famas</a> de <a href ="https://es.wikipedia.org/wiki/Julio_Cort%C3%A1zar">Julio Cortázar</a>.</p>
<p>El tiraje del libro es por definición infinito y cada copia será única.</p>
<p>La distancia de Levenshtein, distancia de edición o distancia entre palabras es un algoritmo que opera en los correctores ortográficos. Es el número mínimo de operaciones requeridas para transformar una palabra en otra. Una operación puede ser una inserción, eliminación o la sustitución de un carácter. El algoritmo fue una invención del científico ruso Vladimir Levenshtein en 1965.</p>
</section>
<section lang="en">
<!-- Engels -->
<h1>Levenshtein Distance reads Cortázar</h1>
<p>'Levenshtein Distance reads Cortázar' is the fifth chapter of <a href = "https://www.tabakalera.eus/es/agora-cemento-codigo">ÁGORA / CEMENTO / CÓDIGO</a>, an online exhibition curated by <a href ="https://www.tabakalera.eus/es/lekutan">Lekutan</a>, within the programme of Komisario Berriak supported by <a href = "https://www.tabakalera.eus">Tabakalera</a>. <a href = "https://www.anaisberck.be">Anaïs Berck</a> presents herewith a first version of a first book of the publishing house 'Algoliterary Publishing: making kin with trees'. In this publishing house the authors are algorithms, presented with their contexts and codes; and the books present the narrative point of view of the algorithm.</p>
<p>The author of this book is the algorithm <a href ="https://en.wikipedia.org/wiki/Levenshtein_distance">Levenhstein Distance</a>, the subject is the eucalyptus tree in "Fama y eucalipto", an excerpt from Historias de Cronopios y de Famas by <a href ="https://en.wikipedia.org/wiki/Julio_Cort%C3%A1zar">Julio Cortázar</a>.</p>
<p>The printrun of the book is by definition infinite and each copy is unique.</p>
<p>Levenshtein distance, edit distance or word distance is an algorithm that operates in spell checkers. It is the minimum number of operations required to transform one word into another. An operation can be an insertion, deletion or substitution of a character. The algorithm was an invention of Russian scientist Vladimir Levenshtein in 1965.</p>
</section>
</section>
<script>
(function () {
var toggles = document.querySelectorAll('[data-target-lang]');
var panel = document.querySelector('.panel--about');
for (let i = 0; i < toggles.length; i++) {
toggles[i].addEventListener('click', function () {
panel.setAttribute('lang', this.dataset.targetLang)
});
}
})();
function getFilename (headers, fallback) {
if (headers.has('Content-Disposition')) {
header = headers.get('Content-Disposition')
matches = header.match(/filename="(.+)"/)
if (matches.length == 2) {
return matches[1]
}
}
return fallback;
}
(function () {
if (fetch) {
var form = document.querySelector('form'),
button = form.querySelector('button'),
messageWorking = document.querySelector('[data-message="working"]'),
messageError = document.querySelector('[data-message="error"]');
form.addEventListener('submit', function (e) {
e.preventDefault();
button.disabled = true;
delete messageError.dataset.active;
messageWorking.dataset.active = true;
const data = new FormData(form);
fetch(form.action, {
method: "POST",
body: data
}).then(function (r) {
console.log(r);
r.blob().then(function (blob) {
var filename = getFilename(r.headers, 'La distancia de Levenshtein.pdf');
const a = document.createElement('a');
a.setAttribute('href', URL.createObjectURL(blob));
a.setAttribute('download', filename);
if (document.createEvent) {
const event = document.createEvent('MouseEvents');
event.initEvent('click', true, true);
a.dispatchEvent(event);
}
else {
a.click();
}
delete messageWorking.dataset.active;
button.disabled = false;
});
}).catch(function() {
delete messageWorking.dataset.active;
messageError.dataset.active = true;
button.disabled = false;
});
});
}
})();
</script>
</body>
</html>

@ -0,0 +1,402 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>La Distancia de Levenshtein lee a Cortázar {{ edition_count }}</title>
<style>
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
</style>
<style>
@font-face {
font-family: XanhMono;
src: url(file://{{ BASEDIR }}/static/fonts/XanhMono-Regular.woff2) format('woff2'),
url(file://{{ BASEDIR }}/static/fonts/XanhMono-Regular.woff) format('woff'),
url(file://{{ BASEDIR }}/static/fonts/XanhMono-Regular.ttf) format('truetype');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: XanhMono;
src: url(file://{{ BASEDIR }}/static/fonts/XanhMono-Italic.woff2) format('woff2'),
url(file://{{ BASEDIR }}/static/fonts/XanhMono-Italic.woff) format('woff'),
url(file://{{ BASEDIR }}/static/fonts/XanhMono-Italic.ttf) format('truetype');
font-weight: 400;
font-style: italic;
}
html, body {
font-family: XanhMono;
font-size: 8.15pt;
line-height: 12pt;
}
body {
margin-left: 9rem;
}
input, select, option, button {
font: inherit;
}
h1 {
font-size: 18pt;
line-height: 26pt;
margin-bottom: 12pt;
}
h2 {
font-size: 14pt;
line-height: 18pt;
break-before: page;
margin-bottom: 12pt;
}
h3 {
font-size: 10pt;
line-height: 12.5pt;
break-before: page;
margin-top: 6pt;
margin-bottom: 6pt;
}
h3.extra-space {
margin-top: 25pt;
}
h1 + h2,
h2 + h3,
.avoid-break {
break-before: avoid;
}
a {
color: currentColor;
}
p, ul, ol {
max-width: 40rem;
margin-bottom: 12pt;
}
h1, h2, h3 {
max-width: 35rem;
}
pre {
font-style: italic;
margin-left: -9rem;
margin-top: 12pt;
}
blockquote {
font-style: italic;
}
footer {
font-style: normal;
font-size: 7pt
}
pre.normal-flow {
margin-left: 0;
}
.two-col {
columns: 2;
margin-left: -8rem;
column-fill: auto;
}
.two-col pre {
margin-left: 0;
margin-top: 0;
orphans: 3;
widows: 3;
}
ul, ol {
margin-top: 12pt;
}
li {
position: relative;
}
ol {
counter-reset: list-counter 0;
}
ol li {
counter-increment: list-counter 1;
}
ol > li {
margin-bottom: 12pt;
}
ol li:before {
position: absolute;
left: -1.15em;
content: counter(list-counter) '.';
}
ol ol {
margin-left: 2em;
counter-reset: sublist-counter;
}
ol ol li {
counter-increment: sublist-counter 1;
}
ol ol li:before {
left: -2.3em;
content: counter(list-counter) '.' counter(sublist-counter);
}
ul li:before {
content: '';
position: absolute;
left: -1em;
}
@page {
margin: 15mm 15mm;
}
@page:left {
@bottom-left {
content: counter(page);
}
}
@page:right {
@bottom-right {
content: counter(page);
}
}
@page:first {
@bottom-right {
content: '';
}
@bottom-left {
content: '';
}
}
</style>
</head>
<body>
<!--title -->
<pre>
{{ fragment_cover_map }}
</pre>
<h1>La Distancia de Levenshtein<br>lee a Cortázar</h1>
<p>
Generado el {{ date }} a las {{ time }}, N⁰ {{ edition_count}}
</p>
<!--index -->
<h2>Índice</h2>
<ol>
<li>Introducción</li>
<li>Lectura de Cortázar
<ol>
<li>Fragmento original</li>
<li>Fragmento adaptado</li>
<li>Mapa del bosque</li>
<li>Matriz de nuevas especies intermediarias</li>
<li>Poesía repetitiva</li>
</ol>
</li>
<li>Descripción general de La Distancia de Levenshtein</li>
<li>La Distancia de Levenshtein explicada de manera técnica</li>
<li>Código</li>
<li>Créditos</li>
</ol>
<!--introduction -->
<h2>1. Introducción</h2>
<p>La Distancia de Levenshtein lee a Cortázar es la primera versión del primer libro en la 'Editorial Algoliteraria: crear alianzas con los árboles'.</p>
<p>El autor de este libro es el algoritmo <a href ="https://es.wikipedia.org/wiki/Distancia_de_Levenshtein">La Distancia de Levenhstein</a>, el tema es el eucalipto en "Fama y eucalipto", un fragmento de <a href ="https://es.wikipedia.org/wiki/Historias_de_cronopios_y_de_famas">Historias de Cronopios y de Famas</a> de <a href ="https://es.wikipedia.org/wiki/Julio_Cort%C3%A1zar">Julio Cortázar</a>.</p>
<p>El libro es por definición infinito y cada copia es única.</p>
<p><a href = "https://www.anaisberck.be">Anaïs Berck</a> es un seudónimo y representa una colaboración entre humanos, algoritmos y árboles. <a href = "https://www.anaisberck.be">Anaïs Berck</a> explora las especificidades de la inteligencia humana en compañía de las inteligencias artificiales y vegetales. En junio 2021, durante una residencia en Medialab Prado en Madrid, Anaïs Berck desarrollará un prototipo de la Editorial Algoliteraria, en la que los algoritmos son los autores de libros inusuales. La residencia fue concedida por el programa "Residencia Cultura Digital" iniciado por el Gobierno Flamenco.</p>
<p>En esta obra Anaïs Berck está representadx por:</p>
<ul>
<li>el algoritmo <a href ="https://es.wikipedia.org/wiki/Distancia_de_Levenshtein">La Distancia de Levenhstein</a> del cual se encuentra una descripción en este libro</li>
<li>el eucalipto en <a href ="https://es.wikipedia.org/wiki/Historias_de_cronopios_y_de_famas">Historias de Cronopios y de Famas</a> de <a href ="https://es.wikipedia.org/wiki/Julio_Cort%C3%A1zar">Julio Cortázar</a>, publicada en 1962 por la Editorial Minotauro</li>
<li>los seres humanos An Mertens y Gijs de Heij. An ha publicado varios libros, como escritora de ficción y como artista e investigadora en <a href="https://constantvzw.org/">Constant</a>, una organización para el arte experimental y los medios de comunicación en Bruselas de la que es miembro desde 2008. Gijs es programador y diseñador, forma parte del colectivo de diseñadores, <a href="http://osp.kitchen/">Open Source Publishing</a> en Bruselas. Ambxs son miembrxs de <a href="https://algolit.net/">Algolit</a>, un grupo de experimentación artística en Bruselas alrededor de algoritmos y textos libres.</li>
</ul>
<!--Reading Cortazar -->
<h2>2. Lectura de Cortázar</h2>
<!--Reading Cortazar -original text -->
<h3>2.1. Fragmento original</h3>
<blockquote>
<p>
Un fama anda por el bosque y aunque no necesita leña mira codiciosamente los árboles. Los árboles tienen un miedo terrible porque conocen las costumbres de los famas y temen lo peor. En medio de todos está un eucalipto hermoso, y el fama al verlo da un grito de alegría y baila tregua y baila catala en torno del perturbado eucalipto, diciendo así:
</p>
<p>
Hojas antisépticas, invierno con salud, gran higiene.
</p>
<p>
Saca un hacha y golpea al eucalipto en el estómago, sin importársele nada. El eucalipto gime, herido de muerte, y los otros árboles oyen que dice entre suspiros:
</p>
<p>
Pensar que este imbécil no tenía más que comprarse unas pastillas Valda.
</p>
<footer>Historias de Cronopios y Famas de Julio Cortázar, publicada en 1962 por la Editorial Minotauro </footer>
</blockquote>
<!--Reading Cortazar - rewritten text -->
<h3 class="avoid-break extra-space">2.2. Fragmento adaptado</h3>
<!--OUTPUT SCRIPT -->
<pre class="normal-flow">{{ new_fragment }}</pre>
<!--Reading Cortazar - map of the woods -->
<h3>2.3. Mapa del bosque</h3>
<p>Las distancias entre el árbol principal que has elegido para tu fragmento y las zonas de otras especies de árboles en el bosque, según La Distancia de Levenshtein:</p>
<!--OUTPUT SCRIPT -->
<pre>
{{ forest_map }}</pre>
<!--Reading Cortazar - table of intermediary species-->
<h3>2.4. Matriz de nuevas especies intermediarias</h3>
<p>La Distancia de Levenshtein crea una matriz con las dos especies. En esta matriz calcula para cada celda la distancia entre los elementos distintos de las dos palabras.</p>
<p>En realidad la matriz se llena de números que representan las operaciones necesarias para cambiar un elemento al otro. Las posibles operaciones son insertar, borrar o sustituir una letra. En vez de los números, esta matriz se llena con las distintas especies intermediarias que el algoritmo crea insertando, borrando o sustituyendo letras.</p>
<!--OUTPUT SCRIPT -->
<pre>
{{ table_of_intermediary_species }}</pre>
<!--Reading Cortazar - repetitive poetry -->
<h3>2.5. Poesía repetitiva</h3>
<!--OUTPUT SCRIPT -->
<div class="two-col">
<pre>{{ repetitive_poetry }}</pre>
</div>
<!--General description algorithm-->
<h2>3. Descripción general de La Distancia de Levenshtein</h2>
<p>La Distancia de Levenshtein es un algoritmo que mide la diferencia entre dos palabras o dos grupos de letras. También se denomina la 'distancia de edición'. La Distancia de Levenshtein entre dos palabras es el número mínimo de acciones necesarias para cambiar una palabra por otra. Las diferentes acciones posibles son la inserción, la supresión o la substitución de una sola letra. Por ejemplo, la Distancia de Levenshtein entre 'más' y 'mes' es 1, ya que se sustituye 'á' por 'e'.</p>
<p>El algoritmo recibió el nombre de su creador, Vladimir Levenshtein, un matemático y científico ruso de origen judío cuya principal área de investigación era la teoría de la información y los códigos de corrección de errores. Trabajó en el Instituto Kéldysh de Matemática Aplicada en Moscú. Falleció en el 2017 a la edad de 82 años. Lanzó el algoritmo en 1965 'para considerar el problema de construir códigos óptimos capaces de corregir supresiones, inserciones e inversiones'.</p>
<p>La Distancia de Levenshtein opera en softwares como los correctores ortográficos y por consecuencia en programas de traducción asistida por computadora. También se puede encontrar la Distancia de Levenshtein en buscadores de información donde detecta las palabras más similares a la palabra entrada erróneamente.</p>
<p>Su actividad se extiende a campos menos evidentes como la detección de plagiarismo, el análisis de ADN, el reconocimiento automático de voz, el reconocimiento óptico de caracteres en el análisis de textos escaneados (OCR), el reconocimiento de la escritura a mano, la detección de hoax emails o la asistencia de venta y compras en el mercado de acciones.</p>
<p>A veces la Distancia de Levenshtein llega a descubrimientos sorprendentes. Así en 1995, Kessler aplicó el algoritmo a la comparación de dialectos irlandeses. Demostró que era un método exitoso para medir las distancias fonéticas entre los dialectos. A partir de las distancias lingüísticas entre variedades dialectales, se pueden encontrar áreas dialectales. Más innovadora era la posibilidad de dibujar mapas dialectales que reflejan el hecho de que las áreas dialectales deben considerarse como continuas y no como áreas separadas por fronteras nítidas.</p>
<p>Fuentes:</p>
<ul>
<li>Vladimir Levenshtein, <a href="https://nymity.ch/sybilhunting/pdf/Levenshtein1966a.pdf">Binary codes capable of correcting deletions, insertions, and reversals</a>; Cybernetics and Control Theory, vol. 10 nr. 8, February 1966.</li>
<li><a href = "https://en.wikipedia.org/wiki/Vladimir_Levenshtein">Vladimir Levenhstein</a> + <a href="https://en.wikipedia.org/wiki/Levenshtein_distance">Distancia de Levenshtein</a> en Wikipedia.</li>
<li>RYBN, ADMXI, <a href = "http://www.rybn.org/ANTI/ADMXI/documentation/ALGORITHM_DOCUMENTATION/HARMONY_OF_THE_SPEARS/LEVENSHTEIN_EDIT_DISTANCE/ABOUT/Wikipedia_Levenshtein_Edit_Distance.pdf">Levenshtein Edit Distance</a>.</li>
<li>Abhi Dattasharma, Praveen Kumar Tripathi and Sridhar G, <a href="http://www.rybn.org/ANTI/ADMXI/documentation/ALGORITHM_DOCUMENTATION/HARMONY_OF_THE_SPEARS/LEVENSHTEIN_EDIT_DISTANCE/FINANCIAL_USES/2008_Identifying_Stock_Similarity_Based_on_Multi-event_Episodes.pdf">Identifying Stock Similarity Based on Multi-event Episodes</a>, in Seventh Australasian Data Mining Conference, 2008, Glenelg, Australia.</li>
<li>S. Dutta Chowdhury; U. Bhattacharya; S.K. Parui, Online Handwriting Recognition Using Levenshtein Distance Metric, in 12th International Conference on Document Analysis and Recognition, 2013, USA.</li>
<li>Yoke Yie Chen, Suet-Peng Yong, and Adzlan Ishak, Email Hoax Detection System Using Levenshtein Distance Method, in Journal of Computers, vol. 9, nr 2, February 2014.</li>
<li>Charlotte Gooskens and Wilbert Heeringa, <a href =" http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.414.9927&rep=rep1&type=pdf">Perceptive evaluation of Levenshtein dialect distancemeasurements using Norwegian dialect data</a>, in Language Variation and Change, nr.16, 2004, 189207. Cambridge University Press.</li>
</ul>
<!--Technical description algorithm-->
<h2>4. La Distancia de Levenshtein explicada de manera técnica</h2>
<p>Los seres humanos pueden reescribir una palabra y contar fácilmente el nombre de cambios que son necesarios para transformar una palabra en otra. Le invitamos a escribir la palabra máquina en una hoja, seguida por la palabra humanx en la línea siguiente. Sabiendo que sólo se puede insertar, borrar o sustituir una letra, ¿cuántas operaciones necesitaría hacer para reescribir la palabra máquina en humanx?</p>
<p>Para que tenga una idea del modo de acción del algoritmo Distancia de Levenshtein, le describimos aquí los diferentes pasos que realiza el algoritmo para transformar la palabra máquina en humanx.</p>
<p>Para la palabra máquina, analiza primero los posibles elementos. Son m, má, máq, máqu, máqui, máquin y máquina para un total de siete elementos. Para la palabra humanx, los elementos son h, hu, hum, huma, human y humanx para un total de seis elementos. Esto crea una matriz con 7 filas y 6 columnas. En esta matriz de distancia calculará para cada celda la distancia entre los elementos de las dos palabras.</p>
<p>Comienza con el primer elemento de la palabra máquina que es m, lo compara con los seis elementos de la palabra humanx. El primero será h. ¿Cuál es la distancia de Levenshtein entre m y h? Lo que tiene que hacer es sustituir el carácter m por h, entonces la distancia es 1.</p>
<p>Pasa al siguiente elemento de la palabra humanx que es hu. ¿Cuál es la distancia de Levenshtein entre m y hu? Como el elemento contiene un solo carácter y hu contiene más de un carácter, puede estar 100% segurx de que tiene que insertar un nuevo carácter. Para transformar m en hu, primero el carácter m es reemplazado por h, y luego añade u. Para transformar m en hu, la distancia es 2.</p>
<p>Ahora pasa al tercer elemento. ¿Cuál es la distancia entre m y hum? Hace lo mismo que arriba, pero tiene que añadir otro carácter, m, también. La distancia final entonces es 3.</p>
<p>Continúa hasta que haya calculado la distancia entre el primer elemento de la palabra máquina, o m, y los 6 elementos de la segunda palabra humanx. Las distancias son simplemente 1, 2, 3, 4 y 5; simplemente aumentan de 1.</p>
<p>Después de calcular las distancias entre el primer prefijo de la primera palabra y todos los prefijos de la segunda palabra, el proceso continúa calculando las distancias entre los prefijos restantes de la primera palabra y los prefijos de la segunda palabra.</p>
<p>Sigue el proceso con má. Lo compara con los seis elementos de la palabra humanx. El primero será h. ¿Cuál es la distancia de Levenshtein entre má y h? Lo que tiene que hacer es sustituir el carácter m por h y borrar el carácter á. Entonces la distancia es 2. Pasa al siguiente elemento de la palabra humanx que es hu. ¿Cuál es la distancia de Levenshtein entre má y hu? Sustituye el carácter m por h y la letra á por u. La distancia es 2.</p>
<p>Y así rellena la matriz.</p>
<p>En términos de código se podría hablar de un efecto optimizador en la matriz.</p>
<p>Se calcula el valor basado en las tres cifras más cercanas de la celda en la matriz que corresponde a los caracteres que se comparan: horizontal, vertical, diagonal.</p>
<p>Si las letras son iguales, se elige el valor mínimo de las tres.</p>
<p>Si las letras son distintas, se elige el valor mínimo de las tres y se añade 1.</p>
<p>El último valor de la serie de cuentas es el que representa la distancia mínima entre las 2 palabras.</p>
<p>En la matriz es el valor situado en la esquina inferior derecha.</p>
<p>Finalmente es asunto de trazar el camino más breve en las transformaciones de una palabra a la otra:</p>
<ul>
<li>el camino diagonal tratando de caracteres diferentes y representa la substitución</li>
<li>el camino diagonal tratando de caracteres iguales no representa ningún cambio</li>
<li>el camino hacia la izquierda representa una inserción</li>
</ul>
<p>Fuente: <a href = "https://blog.paperspace.com/measuring-text-similarity-using-levenshtein-distance/">Blog Paperspace</a></p>
<!--Code-->
<h2>5. Código</h2>
<!--OUTPUT SCRIPT -->
{% for path, source in sources %}
<h3>{{ path }}</h3>
<pre>{{ source }}</pre>
{% endfor %}
<!--Credits-->
<h2>6. Créditos</h2>
<p>Este libro es una creación de <a href = "https://www.anaisberck.be/">Anaïs Berck</a> para <a href = "https://www.tabakalera.eus/es/agora-cemento-codigo">ÁGORA / CEMENTO / CÓDIGO</a>, un proyecto de la <a href ="https://www.tabakalera.eus/es/lekutan">Asociación Cultural LEKUTAN</a> en el <a href = "https://www.tabakalera.eus">Centro Internacional de Cultura Contemporánea Tabakalera</a>, Donostia / San Sebastián.</p>
<p>La copia de este libro es única y el tiraje es por definición infinito.</p>
<!--OUTPUT SCRIPT -->
<p>Esta copia es el número {{ edition_count }} de copias descargadas.</p>
<p>Condiciones colectivas de (re)uso (CC4r), 2021</p>
<p>Copyleft con una diferencia: Se le invita a copiar, distribuir y modificar esta obra bajo los términos de la CC4r: <a href = "https://gitlab.constantvzw.org/unbound/cc4r">https://gitlab.constantvzw.org/unbound/cc4r</a></p>
</body>
</html>
Loading…
Cancel
Save