studio programacion para móviles libro edición desarrollo desarrollar curso aprende aplicaciones r matlab graph distance text-mining

programacion - Visualice distancias entre textos



manual de programacion android pdf (7)

¿Estás haciendo todas las comparaciones por parejas? Depende de cómo se calcula la distancia (similitud), no estoy seguro si es posible hacer una gráfica de dispersión. así que cuando solo tiene 3 archivos de texto para tener en cuenta, su diagrama de dispersión es fácil de hacer (el triángulo con lados equivale a las distancias). pero cuando agrega el cuarto punto, es posible que no pueda colocarlo en una ubicación donde sus distancias a los 3 puntos existentes satisfagan todas las restricciones.

Pero si puedes hacer eso, entonces tienes una solución, solo agrega nuevos puntos una y otra vez ... Creo ... O, si no necesitas las distancias en la gráfica de dispersión para ser preciso, puedes simplemente hacer una web y etiquetar la distancia.

Estoy trabajando en un proyecto de investigación para la escuela. He escrito un software de minería de textos que analiza textos legales en una colección y arroja un puntaje que indica qué tan similares son. Ejecuté el programa para comparar cada texto con cualquier otro texto, y tengo datos como este (aunque con muchos más puntos):

codeofhammurabi.txt crete.txt 0.570737 codeofhammurabi.txt iraqi.txt 1.13475 codeofhammurabi.txt magnacarta.txt 0.945746 codeofhammurabi.txt us.txt 1.25546 crete.txt iraqi.txt 0.329545 crete.txt magnacarta.txt 0.589786 crete.txt us.txt 0.491903 iraqi.txt magnacarta.txt 0.834488 iraqi.txt us.txt 1.37718 magnacarta.txt us.txt 1.09582

Ahora tengo que trazarlos en un gráfico. Puedo invertir fácilmente los puntajes de modo que un pequeño valor ahora indique textos que sean similares y un valor grande indique textos que son diferentes: el valor puede ser la distancia entre puntos en un gráfico que representa los textos.

codeofhammurabi.txt crete.txt 1.75212 codeofhammurabi.txt iraqi.txt 0.8812 codeofhammurabi.txt magnacarta.txt 1.0573 codeofhammurabi.txt us.txt 0.7965 crete.txt iraqi.txt 3.0344 crete.txt magnacarta.txt 1.6955 crete.txt us.txt 2.0329 iraqi.txt magnacarta.txt 1.1983 iraqi.txt us.txt 0.7261 magnacarta.txt us.txt 0.9125

VERSIÓN CORTA: Los valores directamente superiores son distancias entre puntos en un diagrama de dispersión (1.75212 es la distancia entre el punto codeofhammurabi y el punto crete). Puedo imaginar un gran sistema de ecuaciones con círculos que representan las distancias entre puntos. ¿Cuál es la mejor manera de hacer este gráfico? Tengo MATLAB, R, Excel y acceso a casi cualquier software que pueda necesitar.

Si puedes incluso apuntarme en una dirección, estaré infinitamente agradecido.


Aquí hay una solución potencial para Matlab:

Puede organizar sus datos en una matriz de similitud formal S 5x5 donde el elemento S (i, j) representa su similitud (o desemejanza) entre el documento i y el documento j . Suponiendo que su medida de distancia es una métrica real, puede aplicar escalamiento multidimensional a esta matriz a través de mdscale (S, 2) .

Esta función intentará encontrar una representación dimensional de 5x2 de sus datos que preserve la similitud (o desemejanza) entre las clases que se encuentran en las dimensiones superiores. A continuación, puede visualizar estos datos como un diagrama de dispersión de 5 puntos.

También podría probar esto usando mdscale (S, 3) para proyectar en una matriz dimensional de 5x3 que luego puede visualizar con plot3 ().


Este fragmento de Matlab debería funcionar si quieres probar una vista de barra 3D:

% Load data from file ''dist.dat'', with values separated by spaces fid = fopen(''dist.dat''); data = textscan( ... fid, ''%s%s%f'', ... ''Delimiter'', '' '', ... ''MultipleDelimsAsOne'', true ... ); fclose(fid); % Find all unique sources text_bodies = unique(reshape([data{1:2}],[],1)); % Iterate trough the records and complete similarity matrix N = numel(text_bodies); similarity = NaN(N,N); for k = 1:size(data{1},1) n1 = find(strcmp(data{1}{k}, text_bodies)); n2 = find(strcmp(data{2}{k}, text_bodies)); similarity(n1, n2) = data{3}(k); % Symmetrical part ignored end; % Display #D bar chart bar3(similarity);


Podría hacer un gráfico de red usando igraph. El diseño de Fruchterman-Reingold tiene un parámetro para proporcionar pesos de borde. Los pesos más grandes que 1 resultan en más "atracción" a lo largo de los bordes, los pesos inferiores a 1 hacen lo contrario. En su ejemplo, crete.txt tiene la distancia más baja y se encuentra en el medio y tiene bordes más pequeños a otros vértices. De hecho, está más cerca de iraqi.txt. Tenga en cuenta que debe invertir los datos de E (g) $ peso para obtener las distancias correctas.

data1 <- read.table(text=" codeofhammurabi.txt crete.txt 0.570737 codeofhammurabi.txt iraqi.txt 1.13475 codeofhammurabi.txt magnacarta.txt 0.945746 codeofhammurabi.txt us.txt 1.25546 crete.txt iraqi.txt 0.329545 crete.txt magnacarta.txt 0.589786 crete.txt us.txt 0.491903 iraqi.txt magnacarta.txt 0.834488 iraqi.txt us.txt 1.37718 magnacarta.txt us.txt 1.09582") par(mar=c(3,7,3.5,5), las=1) library(igraph) g <- graph.data.frame(data1, directed = FALSE) E(g)$weight <- 1/data1[,3] #inversed, high weights = more attraction along the edges l <- layout.fruchterman.reingold(g, weights=E(g)$weight) plot(g, layout=l)


Si quiere círculos que representen las distancias entre puntos, esto funcionaría en R (utilicé la primera tabla en su ejemplo):

data1 <- read.table(text=" codeofhammurabi.txt crete.txt 0.570737 codeofhammurabi.txt iraqi.txt 1.13475 codeofhammurabi.txt magnacarta.txt 0.945746 codeofhammurabi.txt us.txt 1.25546 crete.txt iraqi.txt 0.329545 crete.txt magnacarta.txt 0.589786 crete.txt us.txt 0.491903 iraqi.txt magnacarta.txt 0.834488 iraqi.txt us.txt 1.37718 magnacarta.txt us.txt 1.09582") par(mar=c(3,7,3.5,5), las=1) symbols(data1[,1],data1[,2], circles=data1[,3], inches=0.55, bg="lightblue", xaxt="n", yaxt="n", ylab="") axis(1, at=data1[,1],labels=data1[,1]) axis(2, at=data1[,2],labels=data1[,2]) text(data1[,1], data1[,2], round(data1[,3],2), cex=0.9)


Sus datos son realmente distancias (de alguna forma) en el espacio multivariable abarcado por el corpus de palabras contenido en los documentos. Los datos de disimilitud como estos a menudo se ordenan para proporcionar el mejor mapeo k- d de las diferencias. El análisis de coordenadas principales y el escalamiento multidimensional no métrico son dos de estos métodos. Sugeriría que grafiques los resultados de aplicar uno u otro de estos métodos a tus datos. Proporciono ejemplos de ambos a continuación.

Primero, cargue los datos que suministró (sin etiquetas en esta etapa)

con <- textConnection("1.75212 0.8812 1.0573 0.7965 3.0344 1.6955 2.0329 1.1983 0.7261 0.9125 ") vec <- scan(con) close(con)

Lo que efectivamente tienes es la siguiente matriz de distancia:

mat <- matrix(ncol = 5, nrow = 5) mat[lower.tri(mat)] <- vec colnames(mat) <- rownames(mat) <- c("codeofhammurabi","crete","iraqi","magnacarta","us") > mat codeofhammurabi crete iraqi magnacarta us codeofhammurabi NA NA NA NA NA crete 1.75212 NA NA NA NA iraqi 0.88120 3.0344 NA NA NA magnacarta 1.05730 1.6955 1.1983 NA NA us 0.79650 2.0329 0.7261 0.9125 NA

R, en general, necesita un objeto disímil de la clase "dist" . Podríamos usar as.dist(mat) ahora para obtener dicho objeto, o podríamos saltear la creación de mat e ir directamente al objeto "dist" como este:

class(vec) <- "dist" attr(vec, "Labels") <- c("codeofhammurabi","crete","iraqi","magnacarta","us") attr(vec, "Size") <- 5 attr(vec, "Diag") <- FALSE attr(vec, "Upper") <- FALSE > vec codeofhammurabi crete iraqi magnacarta crete 1.75212 iraqi 0.88120 3.03440 magnacarta 1.05730 1.69550 1.19830 us 0.79650 2.03290 0.72610 0.91250

Ahora que tenemos un objeto del tipo correcto, podemos ordenarlo. R tiene muchos paquetes y funciones para hacer esto (ver las vistas de tareas multivariantes o de entornos en CRAN), pero usaré el paquete vegano ya que estoy algo familiarizado con él ...

require("vegan")

Coordenadas principales

Primero, explico cómo hacer un análisis de coordenadas principales en sus datos usando vegan .

pco <- capscale(vec ~ 1, add = TRUE) pco > pco Call: capscale(formula = vec ~ 1, add = TRUE) Inertia Rank Total 10.42 Unconstrained 10.42 3 Inertia is squared Unknown distance (euclidified) Eigenvalues for unconstrained axes: MDS1 MDS2 MDS3 7.648 1.672 1.098 Constant added to distances: 0.7667353

El primer eje PCO es con diferencia el más importante a la hora de explicar las diferencias entre textos, tal como lo muestran los valores propios. Ahora se puede producir un gráfico de ordenación trazando los vectores propios del PCO, utilizando el método de plot

plot(pco)

que produce

Escalado multidimensional no métrico

Una escala multidimensional no métrica (nMDS) no intenta encontrar una representación dimensional baja de las distancias originales en un espacio euclidiano. En su lugar, trata de encontrar un mapeo en k dimensiones que conserve mejor el orden jerárquico de las distancias entre observaciones. No existe una solución cerrada a este problema (a diferencia del PCO aplicado anteriormente) y se requiere un algoritmo iterativo para proporcionar una solución. Se recomienda iniciar al azar para asegurarte de que el algoritmo no ha convergido a una solución óptima localmente inferior a la óptima. La función metaMDS de Vegan incorpora estas características y más. Si quiere nMDS antiguo simple, entonces vea isoMDS en el paquete MASS .

set.seed(42) sol <- metaMDS(vec) > sol Call: metaMDS(comm = vec) global Multidimensional Scaling using monoMDS Data: vec Distance: user supplied Dimensions: 2 Stress: 0 Stress type 1, weak ties No convergent solutions - best solution after 20 tries Scaling: centring, PC rotation Species: scores missing

Con este pequeño conjunto de datos, podemos representar esencialmente el orden jerárquico de las diferencias a la perfección (de ahí la advertencia, no se muestra). Se puede lograr un diagrama usando el método de plot

plot(sol, type = "text", display = "sites")

que produce

En ambos casos, la distancia en la trama entre muestras es la mejor aproximación bidimensional de su desemejanza. En el caso de la trama PCO, es una aproximación 2D de la diferencia real (se necesitan 3 dimensiones para representar todas las diferencias por completo), mientras que en la gráfica nMDS, la distancia entre muestras en la trama refleja la diferencia de rango no la diferencia real entre las observaciones. Pero esencialmente las distancias en la trama representan las diferencias calculadas. Los textos que están muy juntos son muy similares, los textos ubicados muy separados en la trama son los más diferentes entre sí.


Si la pregunta es ''¿cómo puedo hacer algo como lo hizo este tipo ?'' (del comentario de xiii1408 a la pregunta), entonces la respuesta es usar el algoritmo Force Atlas 2 de Gephi en las distancias euclidianas de las probabilidades posteriores del tema del documento .

"Este tipo" es Matt Jockers, que es un investigador innovador en humanidades digitales. Él ha documentado algunos de sus métodos en su blog y en otros lugares , etc. Jockers trabaja principalmente en R y comparte parte de su código . Su flujo de trabajo básico parece ser:

  1. divide texto plano en trozos de 1000 palabras,
  2. eliminar las palabras vacías (no detener),
  3. hacer etiquetas de voz parcial y mantener solo los nombres,
  4. construir un modelo de tema (usando LDA),
  5. calcular las distancias euclidianas entre los documentos en función de las proporciones del tema, subconjuntos de las distancias para mantener solo los que están por debajo de un cierto umbral, y luego
  6. visualizar con un gráfico dirigido por la fuerza

Aquí hay un ejemplo reproducible a pequeña escala en R (con una exportación a Gephi) que podría estar cerca de lo que hizo Jockers:

#### prepare workspace # delete current objects and clear RAM rm(list = ls(all.names = TRUE)) gc()

Obtener datos...

#### import text # working from the topicmodels package vignette # using collection of abstracts of the Journal of Statistical Software (JSS) (up to 2010-08-05). install.packages("corpus.JSS.papers", repos = "http://datacube.wu.ac.at/", type = "source") data("JSS_papers", package = "corpus.JSS.papers") # For reproducibility of results we use only abstracts published up to 2010-08-05 JSS_papers <- JSS_papers[JSS_papers[,"date"] < "2010-08-05",]

Limpiar y remodelar ...

#### clean and reshape data # Omit abstracts containing non-ASCII characters in the abstracts JSS_papers <- JSS_papers[sapply(JSS_papers[, "description"], Encoding) == "unknown",] # remove greek characters (from math notation, etc.) library("tm") library("XML") remove_HTML_markup <- function(s) tryCatch({ doc <- htmlTreeParse(paste("<!DOCTYPE html>", s), asText = TRUE, trim = FALSE) xmlValue(xmlRoot(doc)) }, error = function(s) s) # create corpus corpus <- Corpus(VectorSource(sapply(JSS_papers[, "description"], remove_HTML_markup))) # clean corpus by removing stopwords, numbers, punctuation, whitespaces, words <3 characters long.. skipWords <- function(x) removeWords(x, stopwords("english")) funcs <- list(tolower, removePunctuation, removeNumbers, stripWhitespace, skipWords) corpus_clean <- tm_map(corpus, wordLengths=c(3,Inf), FUN = tm_reduce, tmFuns = funcs)

Parte del etiquetado de voz y la subconfiguración de sustantivos ...

#### Part-of-speach tagging to extract nouns only library("openNLP", "NLP") # function for POS tagging tagPOS <- function(x) { s <- NLP::as.String(x) ## Need sentence and word token annotations. a1 <- NLP::Annotation(1L, "sentence", 1L, nchar(s)) a2 <- NLP::annotate(s, openNLP::Maxent_Word_Token_Annotator(), a1) a3 <- NLP::annotate(s, openNLP::Maxent_POS_Tag_Annotator(), a2) ## Determine the distribution of POS tags for word tokens. a3w <- a3[a3$type == "word"] POStags <- unlist(lapply(a3w$features, `[[`, "POS")) ## Extract token/POS pairs (all of them): easy - not needed # POStagged <- paste(sprintf("%s/%s", s[a3w], POStags), collapse = " ") return(unlist(POStags)) } # a loop to do POS tagging on each document and do garbage cleaning after each document # first prepare vector to hold results (for optimal loop speed) corpus_clean_tagged <- vector(mode = "list", length = length(corpus_clean)) # then loop through each doc and do POS tagging # warning: this may take some time! for(i in 1:length(corpus_clean)){ corpus_clean_tagged[[i]] <- tagPOS(corpus_clean[[i]]) print(i) # nice to see what we''re up to gc() } # subset nouns wrds <- lapply(unlist(corpus_clean), function(i) unlist(strsplit(i, split = " "))) NN <- lapply(corpus_clean_tagged, function(i) i == "NN") Noun_strings <- lapply(1:length(wrds), function(i) unlist(wrds[i])[unlist(NN[i])]) Noun_strings <- lapply(Noun_strings, function(i) paste(i, collapse = " ")) # have a look to see what we''ve got Noun_strings[[1]] [8] "variogram model splus user quality variogram model pairs locations measurements variogram nonstationarity outliers variogram fit sets soil nitrogen concentration"

Modelado de temas con asignación latente de Dirichlet ...

#### topic modelling with LDA (Jockers uses the lda package and MALLET, maybe topicmodels also, I''m not sure. I''m most familiar with the topicmodels package, so here it is. Note that MALLET can be run from R: https://gist.github.com/benmarwick/4537873 # put the cleaned documents back into a corpus for topic modelling corpus <- Corpus(VectorSource(Noun_strings)) # create document term matrix JSS_dtm <- DocumentTermMatrix(corpus) # generate topic model library("topicmodels") k = 30 # arbitrary number of topics (they are ways to optimise this) JSS_TM <- LDA(JSS_dtm, k) # make topic model # make data frame where rows are documents, columns are topics and cells # are posterior probabilities of topics JSS_topic_df <- setNames(as.data.frame(JSS_TM@gamma), paste0("topic_",1:k)) # add row names that link each document to a human-readble bit of data # in this case we''ll just use a few words of the title of each paper row.names(JSS_topic_df) <- lapply(1:length(JSS_papers[,1]), function(i) gsub("//s","_",substr(JSS_papers[,1][[i]], 1, 60)))

Calcule distancias euclidianas de un documento a otro usando probabilidades de temas como el ''ADN'' del documento

#### Euclidean distance matrix library(cluster) JSS_topic_df_dist <- as.matrix(daisy(JSS_topic_df, metric = "euclidean", stand = TRUE)) # Change row values to zero if less than row minimum plus row standard deviation # This is how Jockers subsets the distance matrix to keep only # closely related documents and avoid a dense spagetti diagram # that''s difficult to interpret (hat-tip: http://.com/a/16047196/1036500) JSS_topic_df_dist[ sweep(JSS_topic_df_dist, 1, (apply(JSS_topic_df_dist,1,min) + apply(JSS_topic_df_dist,1,sd) )) > 0 ] <- 0

Visualice usando un gráfico dirigido por la fuerza ...

#### network diagram using Fruchterman & Reingold algorithm (Jockers uses the ForceAtlas2 algorithm which is unique to Gephi) library(igraph) g <- as.undirected(graph.adjacency(JSS_topic_df_dist)) layout1 <- layout.fruchterman.reingold(g, niter=500) plot(g, layout=layout1, edge.curved = TRUE, vertex.size = 1, vertex.color= "grey", edge.arrow.size = 0.1, vertex.label.dist=0.5, vertex.label = NA)

Y si desea utilizar el algoritmo Force Atlas 2 en Gephi, simplemente exporte el objeto gráfico R a un archivo graphml y luego ábralo en Gephi y configure el diseño en Force Atlas 2:

# this line will export from R and make the file ''JSS.graphml'' in your working directory ready to open with Gephi write.graph(g, file="JSS.graphml", format="graphml")

Aquí está el diagrama de Gephi con el algoritmo Force Atlas 2: