2009-11-24 5 views
22

J'ai besoin de regrouper des documents texte et j'ai recherché diverses options. On dirait que LingPipe peut regrouper du texte brut sans conversion préalable (en vectoriel, etc.), mais c'est le seul outil que j'ai vu qui prétend explicitement travailler sur des chaînes.Clustering de texte en Python

Y a-t-il des outils Python qui peuvent regrouper du texte directement? Si non, quelle est la meilleure façon de gérer cela?

+1

Informations sur les « clusters texte » est: http://www2.parc.com/istl/projects/ia/sg-clustering.html – fviktor

+0

Si vous avez besoin d'aide sur l'extraction le contenu du texte de divers types de documents, alors s'il vous plaît ajouter une autre question, car c'est une autre partie de la tâche, je pense. Cela permettrait une meilleure séparation des problèmes à mon humble avis. – fviktor

+0

Mallet travaillera également sur des fichiers texte sans que vous ayez à faire quoi que ce soit, en supposant que vous avez un répertoire plein d'entre eux (avec un doc par fichier). Je recommande simplement d'utiliser nltk, une bibliothèque python.Vous aurez besoin de faire un peu de pré-traitement sur les fichiers, mais ce n'est pas douloureux. – ealdent

Répondre

44

La qualité du texte dépend regroupement principalement de deux facteurs:

  1. une certaine notion de similitude entre les documents que vous souhaitez regrouper. Par exemple, il est facile de faire la distinction entre les articles sur les sports et la politique dans l'espace vectoriel via tfidf-cosine-distance. Il est beaucoup plus difficile de regrouper les revues de produits en «bonnes» ou «mauvaises» sur la base de cette mesure.

  2. La méthode de regroupement elle-même. Vous savez combien de cluster il y aura? Ok, utilisez les kmeans. Vous ne vous souciez pas de la précision, mais souhaitez afficher une arborescence pour la navigation dans les résultats de recherche? Utilisez une sorte de clustering hiérarchique.

Il n'y a pas de solution en cluster texte, qui fonctionnerait bien en aucun cas. Et par conséquent, il ne suffit probablement pas de prendre un logiciel de clustering prêt à l'emploi et d'y jeter vos données. Cela dit, voici un code expérimental que j'ai utilisé il y a quelques temps pour jouer avec le groupement de texte. Les documents sont représentés comme des vecteurs tfidf normalisés et la similarité est mesurée en tant que distance en cosinus. La méthode de regroupement elle-même est 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) 

Pour des applications réelles, vous utilisez un tokenizer décent, entiers d'utilisation au lieu de cordes jeton et ne CALC pas un O (n^2) distance matrice ...

2

Il semble possible d'utiliser des outils de ligne de commande UNIX simples pour extraire le contenu textuel de ces documents dans des fichiers texte, puis d'utiliser une solution Python pure pour le clustering réel.

je trouve un fragment de code pour les données de classification en général:

http://www.daniweb.com/code/snippet216641.html

Un paquet python pour cela:

http://python-cluster.sourceforge.net/

Un autre paquet python (utilisé principalement pour la bio-informatique):

http://bonsai.ims.u-tokyo.ac.jp/~mdehoon/software/cluster/software.htm#pycluster

0

Il est Python bibliothèque NLTK qui prend en charge l'analyse linguistique, y compris le texte de regroupement