diff --git a/.comments/20180426_155319.jpg.xml b/.comments/20180426_155319.jpg.xml new file mode 100644 index 0000000..49ebe1a --- /dev/null +++ b/.comments/20180426_155319.jpg.xml @@ -0,0 +1,7 @@ + + + + User comments + + + diff --git a/.comments/imagen_proyecto.jpg.xml b/.comments/imagen_proyecto.jpg.xml new file mode 100644 index 0000000..cfcc0cd --- /dev/null +++ b/.comments/imagen_proyecto.jpg.xml @@ -0,0 +1,7 @@ + + + + charset="Ascii" User comments + + + diff --git a/60_trees_with_english_names.txt b/60_trees_with_english_names.txt new file mode 100644 index 0000000..c2f0c46 --- /dev/null +++ b/60_trees_with_english_names.txt @@ -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 diff --git a/__pycache__/app.cpython-38.pyc b/__pycache__/app.cpython-38.pyc new file mode 100644 index 0000000..9295a7c Binary files /dev/null and b/__pycache__/app.cpython-38.pyc differ diff --git a/__pycache__/la_distancia_de_levenshtein_lee_a_cortazar.cpython-38.pyc b/__pycache__/la_distancia_de_levenshtein_lee_a_cortazar.cpython-38.pyc new file mode 100644 index 0000000..8d7f640 Binary files /dev/null and b/__pycache__/la_distancia_de_levenshtein_lee_a_cortazar.cpython-38.pyc differ diff --git a/__pycache__/tabakalera_def.cpython-38.pyc b/__pycache__/tabakalera_def.cpython-38.pyc new file mode 100644 index 0000000..46445ac Binary files /dev/null and b/__pycache__/tabakalera_def.cpython-38.pyc differ diff --git a/app.py b/app.py new file mode 100644 index 0000000..2266b34 --- /dev/null +++ b/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: . + +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' diff --git a/arboles_plural.txt b/arboles_plural.txt new file mode 100644 index 0000000..a5ff491 --- /dev/null +++ b/arboles_plural.txt @@ -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 diff --git a/arboles_simple.txt b/arboles_simple.txt new file mode 100644 index 0000000..32b3a32 --- /dev/null +++ b/arboles_simple.txt @@ -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 diff --git a/arboles_simple_fem.txt b/arboles_simple_fem.txt new file mode 100644 index 0000000..bedac3e --- /dev/null +++ b/arboles_simple_fem.txt @@ -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) diff --git a/drawmap.py b/drawmap.py new file mode 100644 index 0000000..7759aff --- /dev/null +++ b/drawmap.py @@ -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) \ No newline at end of file diff --git a/eucalipto_cortazar.txt b/eucalipto_cortazar.txt new file mode 100644 index 0000000..0e479bf --- /dev/null +++ b/eucalipto_cortazar.txt @@ -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. + + + + + diff --git a/html/content.html b/html/content.html new file mode 100644 index 0000000..7605ff4 --- /dev/null +++ b/html/content.html @@ -0,0 +1,242 @@ + + + + + + + Tabakalera + + + + +

+ 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)] +

+

+ This text will be slowly rewritten by similar words.
+ You can choose the degree of similarity for its rewriting by typing a number between 1 and 10 :
+ 5
+ 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']
+ word to be replaced in text: catala
+ replacement word: unas +

+

+ element of original word in text to be compared: c
+ element of replacement word to be compared: u
+ in between word where selected letters of original word replace selected letters of replacement word: cnas
+ difference between c and u : 0 +

+ c → u + [c → u]nas + c <> u 1 +

+ element of original word in text to be compared: c
+ element of replacement word to be compared: un
+ in between word where selected letters of original word replace selected letters of replacement word: cas
+ difference between c and un : 1 +

+

+ element of original word in text to be compared: c
+ element of replacement word to be compared: una
+ in between word where selected letters of original word replace selected letters of replacement word: cs
+ difference between c and una : 2 +

+

+ element of original word in text to be compared: c
+ element of replacement word to be compared: unas
+ in between word where selected letters of original word replace selected letters of replacement word: c
+ difference between c and unas : 3 +

+

+ element of original word in text to be compared: ca
+ element of replacement word to be compared: u
+ in between word where selected letters of original word replace selected letters of replacement word: canas
+ difference between ca and u : 1 +

+

+ element of original word in text to be compared: ca
+ element of replacement word to be compared: un
+ in between word where selected letters of original word replace selected letters of replacement word: caas
+ difference between ca and un : 1 +

+

+ element of original word in text to be compared: ca
+ element of replacement word to be compared: una
+ in between word where selected letters of original word replace selected letters of replacement word: cas
+ difference between ca and una : 2 +

+

+ element of original word in text to be compared: ca
+ element of replacement word to be compared: unas
+ in between word where selected letters of original word replace selected letters of replacement word: ca
+ difference between ca and unas : 3 +

+

+ element of original word in text to be compared: cat
+ element of replacement word to be compared: u
+ in between word where selected letters of original word replace selected letters of replacement word: catnas
+ difference between cat and u : 2 +

+

+ element of original word in text to be compared: cat
+ element of replacement word to be compared: un
+ in between word where selected letters of original word replace selected letters of replacement word: catas
+ difference between cat and un : 2 +

+

+ element of original word in text to be compared: cat
+ element of replacement word to be compared: una
+ in between word where selected letters of original word replace selected letters of replacement word: cats
+ difference between cat and una : 2 +

+

+ element of original word in text to be compared: cat
+ element of replacement word to be compared: unas
+ in between word where selected letters of original word replace selected letters of replacement word: cat
+ difference between cat and unas : 2 +

+

+ element of original word in text to be compared: cata
+ element of replacement word to be compared: u
+ in between word where selected letters of original word replace selected letters of replacement word: catanas
+ difference between cata and u : 3 +

+

+ element of original word in text to be compared: cata
+ element of replacement word to be compared: un
+ in between word where selected letters of original word replace selected letters of replacement word: cataas
+ difference between cata and un : 3 +

+

+ element of original word in text to be compared: cata
+ element of replacement word to be compared: una
+ in between word where selected letters of original word replace selected letters of replacement word: catas
+ difference between cata and una : 3 +

+

+ element of original word in text to be compared: cata
+ element of replacement word to be compared: unas
+ in between word where selected letters of original word replace selected letters of replacement word: cata
+ difference between cata and unas : 3 +

+

+ element of original word in text to be compared: catal
+ element of replacement word to be compared: u
+ in between word where selected letters of original word replace selected letters of replacement word: catalnas
+ difference between catal and u : 4 +

+

+ element of original word in text to be compared: catal
+ element of replacement word to be compared: un
+ in between word where selected letters of original word replace selected letters of replacement word: catalas
+ difference between catal and un : 4 +

+

+ element of original word in text to be compared: catal
+ element of replacement word to be compared: una
+ in between word where selected letters of original word replace selected letters of replacement word: catals
+ difference between catal and una : 4 +

+

+ element of original word in text to be compared: catal
+ element of replacement word to be compared: unas
+ in between word where selected letters of original word replace selected letters of replacement word: catal
+ difference between catal and unas : 3 +

+

+ element of original word in text to be compared: catala
+ element of replacement word to be compared: u
+ in between word where selected letters of original word replace selected letters of replacement word: catalanas
+ difference between catala and u : 5 +

+

+ element of original word in text to be compared: catala
+ element of replacement word to be compared: un
+ in between word where selected letters of original word replace selected letters of replacement word: catalaas
+ difference between catala and un : 5 +

+

+ element of original word in text to be compared: catala
+ element of replacement word to be compared: una
+ in between word where selected letters of original word replace selected letters of replacement word: catalas
+ difference between catala and una : 5 +

+

+ element of original word in text to be compared: catala
+ element of replacement word to be compared: unas
+ in between word where selected letters of original word replace selected letters of replacement word: catala
+ difference between catala and unas : 4 +

+

+ 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'] +

+

+ 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 <> 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. +

+ + + \ No newline at end of file diff --git a/html/makepdf.py b/html/makepdf.py new file mode 100644 index 0000000..04074fd --- /dev/null +++ b/html/makepdf.py @@ -0,0 +1,4 @@ +from weasyprint import HTML + +HTML(filename='content.html')\ + .write_pdf('weasyprint-library.pdf') diff --git a/la_distancia_de_levenshtein_lee_a_cortazar.py b/la_distancia_de_levenshtein_lee_a_cortazar.py new file mode 100644 index 0000000..bd6fc49 --- /dev/null +++ b/la_distancia_de_levenshtein_lee_a_cortazar.py @@ -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: . + +# 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) diff --git a/paper_original_vladimir_levenhstein_1966.pdf b/paper_original_vladimir_levenhstein_1966.pdf new file mode 100644 index 0000000..8ff727b Binary files /dev/null and b/paper_original_vladimir_levenhstein_1966.pdf differ diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e362073 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +WeasyPrint +python-Levenshtein +flask \ No newline at end of file diff --git a/scrape_html_page.py b/scrape_html_page.py new file mode 100644 index 0000000..2ce84f2 --- /dev/null +++ b/scrape_html_page.py @@ -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 + +''' \ No newline at end of file diff --git a/static/fonts/OFL.txt b/static/fonts/OFL.txt new file mode 100644 index 0000000..7229a80 --- /dev/null +++ b/static/fonts/OFL.txt @@ -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. diff --git a/static/fonts/XanhMono-Italic.ttf b/static/fonts/XanhMono-Italic.ttf new file mode 100644 index 0000000..8247acf Binary files /dev/null and b/static/fonts/XanhMono-Italic.ttf differ diff --git a/static/fonts/XanhMono-Italic.woff b/static/fonts/XanhMono-Italic.woff new file mode 100644 index 0000000..36b61c4 Binary files /dev/null and b/static/fonts/XanhMono-Italic.woff differ diff --git a/static/fonts/XanhMono-Italic.woff2 b/static/fonts/XanhMono-Italic.woff2 new file mode 100644 index 0000000..4ebe2c6 Binary files /dev/null and b/static/fonts/XanhMono-Italic.woff2 differ diff --git a/static/fonts/XanhMono-Regular.ttf b/static/fonts/XanhMono-Regular.ttf new file mode 100644 index 0000000..b56a225 Binary files /dev/null and b/static/fonts/XanhMono-Regular.ttf differ diff --git a/static/fonts/XanhMono-Regular.woff b/static/fonts/XanhMono-Regular.woff new file mode 100644 index 0000000..28f8bc8 Binary files /dev/null and b/static/fonts/XanhMono-Regular.woff differ diff --git a/static/fonts/XanhMono-Regular.woff2 b/static/fonts/XanhMono-Regular.woff2 new file mode 100644 index 0000000..f2a5c80 Binary files /dev/null and b/static/fonts/XanhMono-Regular.woff2 differ diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..7c3d405 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,359 @@ + + + + + + + + La Distancia de Levenshtein lee a Cortázar + + +
+

Fama y eucalipto

+
+

+ 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. +

+
Historias de Cronopios y Famas de Julio Cortázar, publicada en 1962 por la Editorial Minotauro
+
+
+
+

A ti

+

+ Para que La Distancia de Levenshtein pueda leer el fragmento y producir un libro único, necesitas cambiar una palabra. +

+

+ 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. +

+
+ + {% for tree in trees %} + + {% endfor %} + +
La Distancia de Levenshtein está escribiendo, por favor espere un momento.
+
Ocurrió un error, inténtalo de nuevo por favor.
+ +
+
+
+ +
+

La Distancia de Levenshtein lee a Cortázar

+

'La Distancia de Levenshtein lee a Cortázar' es el quinto capítulo de ÁGORA / CEMENTO / CÓDIGO, una exposición online comisariada por Lekutan, dentro del programa Komisario Berriak, proyecto apoyado por Tabakalera en Donostia / San Sebastián. Anaïs Berck 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.

+

El autor de este libro es el algoritmo La Distancia de Levenhstein, el tema es el eucalipto en "Fama y eucalipto", un fragmento de Historias de Cronopios y de Famas de Julio Cortázar.

+

El tiraje del libro es por definición infinito y cada copia será única.

+

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.

+
+
+ +

Levenshtein Distance reads Cortázar

+

'Levenshtein Distance reads Cortázar' is the fifth chapter of ÁGORA / CEMENTO / CÓDIGO, an online exhibition curated by Lekutan, within the programme of Komisario Berriak supported by Tabakalera. Anaïs Berck 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.

+

The author of this book is the algorithm Levenhstein Distance, the subject is the eucalyptus tree in "Fama y eucalipto", an excerpt from Historias de Cronopios y de Famas by Julio Cortázar.

+

The printrun of the book is by definition infinite and each copy is unique.

+

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.

+
+
+ + + \ No newline at end of file diff --git a/templates/print.html b/templates/print.html new file mode 100644 index 0000000..435e12e --- /dev/null +++ b/templates/print.html @@ -0,0 +1,402 @@ + + + + + + La Distancia de Levenshtein lee a Cortázar {{ edition_count }} + + + + + +
+{{ fragment_cover_map }}
+    
+    
+    
+    
+    
+    
+

La Distancia de Levenshtein
lee a Cortázar

+

+ Generado el {{ date }} a las {{ time }}, N⁰ {{ edition_count}} +

+ +

Índice

+
    +
  1. Introducción
  2. +
  3. Lectura de Cortázar +
      +
    1. Fragmento original
    2. +
    3. Fragmento adaptado
    4. +
    5. Mapa del bosque
    6. +
    7. Matriz de nuevas especies intermediarias
    8. +
    9. Poesía repetitiva
    10. +
    +
  4. +
  5. Descripción general de La Distancia de Levenshtein
  6. +
  7. La Distancia de Levenshtein explicada de manera técnica
  8. +
  9. Código
  10. +
  11. Créditos
  12. +
+ + +

1. Introducción

+

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'.

+

El autor de este libro es el algoritmo La Distancia de Levenhstein, el tema es el eucalipto en "Fama y eucalipto", un fragmento de Historias de Cronopios y de Famas de Julio Cortázar.

+

El libro es por definición infinito y cada copia es única.

+

Anaïs Berck es un seudónimo y representa una colaboración entre humanos, algoritmos y árboles. Anaïs Berck 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.

+

En esta obra Anaïs Berck está representadx por:

+ + + +

2. Lectura de Cortázar

+ +

2.1. Fragmento original

+
+

+ 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. +

+
Historias de Cronopios y Famas de Julio Cortázar, publicada en 1962 por la Editorial Minotauro
+
+ + +

2.2. Fragmento adaptado

+ +
{{ new_fragment }}
+ + +

2.3. Mapa del bosque

+

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:

+ +
+
+
+
+{{ forest_map }}
+ + +

2.4. Matriz de nuevas especies intermediarias

+

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.

+

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.

+ +
+
+
+{{ table_of_intermediary_species }}
+ + +

2.5. Poesía repetitiva

+ +
+
{{ repetitive_poetry }}
+
+ + +

3. Descripción general de La Distancia de Levenshtein

+

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'.

+

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'.

+

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.

+

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.

+

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.

+

Fuentes:

+ + + + +

4. La Distancia de Levenshtein explicada de manera técnica

+

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?

+

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.

+

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.

+

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.

+

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.

+

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.

+

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.

+

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.

+

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.

+

Y así rellena la matriz.

+

En términos de código se podría hablar de un efecto optimizador en la matriz.

+

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.

+

Si las letras son iguales, se elige el valor mínimo de las tres.

+

Si las letras son distintas, se elige el valor mínimo de las tres y se añade 1.

+

El último valor de la serie de cuentas es el que representa la distancia mínima entre las 2 palabras.

+

En la matriz es el valor situado en la esquina inferior derecha.

+

Finalmente es asunto de trazar el camino más breve en las transformaciones de una palabra a la otra:

+ +

Fuente: Blog Paperspace

+ + +

5. Código

+ + {% for path, source in sources %} +

{{ path }}

+
{{ source }}
+ {% endfor %} + + +

6. Créditos

+

Este libro es una creación de Anaïs Berck para ÁGORA / CEMENTO / CÓDIGO, un proyecto de la Asociación Cultural LEKUTAN en el Centro Internacional de Cultura Contemporánea Tabakalera, Donostia / San Sebastián.

+

La copia de este libro es única y el tiraje es por definición infinito.

+ + +

Esta copia es el número {{ edition_count }} de copias descargadas.

+

Condiciones colectivas de (re)uso (CC4r), 2021

+

Copyleft con una diferencia: Se le invita a copiar, distribuir y modificar esta obra bajo los términos de la CC4r: https://gitlab.constantvzw.org/unbound/cc4r

+ + + \ No newline at end of file