python - unir - recorrer data frame pandas
Agrupar texto en Python (3)
Necesito agrupar algunos documentos de texto y he estado investigando varias opciones. Parece que LingPipe puede agrupar texto simple sin conversión previa (a espacio vectorial, etc.), pero es la única herramienta que he visto que afirma explícitamente que funciona en cadenas.
¿Hay alguna herramienta de Python que pueda agrupar texto directamente? Si no, ¿cuál es la mejor manera de manejar esto?
Existe una biblioteca NLTK Python que admite el análisis lingüístico, incluido el agrupamiento de texto.
La calidad del agrupamiento de texto depende principalmente de dos factores:
Alguna noción de similitud entre los documentos que desea agrupar. Por ejemplo, es fácil distinguir entre artículos sobre deportes y política en el espacio vectorial a través de tfidf-cosine-distance. Es mucho más difícil agrupar las revisiones de productos en "buena" o "mala" según esta medida.
El propio método de agrupamiento. ¿Sabes cuántos grupos habrá? Ok, usa kmeans. ¿No te importa la precisión pero quieres mostrar una buena estructura de árbol para la navegación de los resultados de búsqueda? Utilice algún tipo de agrupación jerárquica.
No existe una solución de agrupación de texto que funcione bien en cualquier circunstancia. Y, por lo tanto, es probable que no sea suficiente sacar un poco de software de agrupación y lanzar sus datos.
Dicho esto, aquí hay un código experimental que usé hace algún tiempo para jugar con el agrupamiento de texto. Los documentos se representan como vectores tfidf normalizados y la similitud se mide como la distancia del coseno. El método de agrupamiento en sí mismo es majorclust .
import sys
from math import log, sqrt
from itertools import combinations
def cosine_distance(a, b):
cos = 0.0
a_tfidf = a["tfidf"]
for token, tfidf in b["tfidf"].iteritems():
if token in a_tfidf:
cos += tfidf * a_tfidf[token]
return cos
def normalize(features):
norm = 1.0 / sqrt(sum(i**2 for i in features.itervalues()))
for k, v in features.iteritems():
features[k] = v * norm
return features
def add_tfidf_to(documents):
tokens = {}
for id, doc in enumerate(documents):
tf = {}
doc["tfidf"] = {}
doc_tokens = doc.get("tokens", [])
for token in doc_tokens:
tf[token] = tf.get(token, 0) + 1
num_tokens = len(doc_tokens)
if num_tokens > 0:
for token, freq in tf.iteritems():
tokens.setdefault(token, []).append((id, float(freq) / num_tokens))
doc_count = float(len(documents))
for token, docs in tokens.iteritems():
idf = log(doc_count / len(docs))
for id, tf in docs:
tfidf = tf * idf
if tfidf > 0:
documents[id]["tfidf"][token] = tfidf
for doc in documents:
doc["tfidf"] = normalize(doc["tfidf"])
def choose_cluster(node, cluster_lookup, edges):
new = cluster_lookup[node]
if node in edges:
seen, num_seen = {}, {}
for target, weight in edges.get(node, []):
seen[cluster_lookup[target]] = seen.get(
cluster_lookup[target], 0.0) + weight
for k, v in seen.iteritems():
num_seen.setdefault(v, []).append(k)
new = num_seen[max(num_seen)][0]
return new
def majorclust(graph):
cluster_lookup = dict((node, i) for i, node in enumerate(graph.nodes))
count = 0
movements = set()
finished = False
while not finished:
finished = True
for node in graph.nodes:
new = choose_cluster(node, cluster_lookup, graph.edges)
move = (node, cluster_lookup[node], new)
if new != cluster_lookup[node] and move not in movements:
movements.add(move)
cluster_lookup[node] = new
finished = False
clusters = {}
for k, v in cluster_lookup.iteritems():
clusters.setdefault(v, []).append(k)
return clusters.values()
def get_distance_graph(documents):
class Graph(object):
def __init__(self):
self.edges = {}
def add_edge(self, n1, n2, w):
self.edges.setdefault(n1, []).append((n2, w))
self.edges.setdefault(n2, []).append((n1, w))
graph = Graph()
doc_ids = range(len(documents))
graph.nodes = set(doc_ids)
for a, b in combinations(doc_ids, 2):
graph.add_edge(a, b, cosine_distance(documents[a], documents[b]))
return graph
def get_documents():
texts = [
"foo blub baz",
"foo bar baz",
"asdf bsdf csdf",
"foo bab blub",
"csdf hddf kjtz",
"123 456 890",
"321 890 456 foo",
"123 890 uiop",
]
return [{"text": text, "tokens": text.split()}
for i, text in enumerate(texts)]
def main(args):
documents = get_documents()
add_tfidf_to(documents)
dist_graph = get_distance_graph(documents)
for cluster in majorclust(dist_graph):
print "========="
for doc_id in cluster:
print documents[doc_id]["text"]
if __name__ == ''__main__'':
main(sys.argv)
Para aplicaciones reales, usaría un tokenizador decente, usaría números enteros en lugar de cadenas de token y no calcularía una matriz de distancia O (n ^ 2) ...
Parece que es posible mediante el uso de herramientas de línea de comandos simples de UNIX para extraer el contenido de texto de esos documentos en archivos de texto, y luego usar una solución Python pura para el agrupamiento real.
Encontré un fragmento de código para agrupar datos en general:
http://www.daniweb.com/code/snippet216641.html
Un paquete de Python para esto:
http://python-cluster.sourceforge.net/
Otro paquete de python (usado principalmente para bioinformática):
http://bonsai.ims.u-tokyo.ac.jp/~mdehoon/software/cluster/software.htm#pycluster