Matriz de co-ocurrencia de la lista de palabras en Python

Tengo una lista de nombres como:

names = ['A', 'B', 'C', 'D'] 

y una lista de documentos, que en cada documento se mencionan algunos de estos nombres.

 document =[['A', 'B'], ['C', 'B', 'K'],['A', 'B', 'C', 'D', 'Z']] 

Me gustaría obtener una salida como una matriz de co-ocurrencias como:

  ABCD A 0 2 1 1 B 2 0 2 1 C 1 2 0 1 D 1 1 1 0 

Hay una solución ( Creación de matriz de co-ocurrencia ) para este problema en R, pero no pude hacerlo en Python. Estoy pensando en hacerlo en Pandas, pero aún no hay progreso!

Obviamente, esto puede extenderse para sus propósitos, pero realiza la operación general en mente:

 import math for a in 'ABCD': for b in 'ABCD': count = 0 for x in document: if a != b: if a in x and b in x: count += 1 else: n = x.count(a) if n >= 2: count += math.factorial(n)/math.factorial(n - 2)/2 print '{} x {} = {}'.format(a, b, count) 
 from collections import OrderedDict document = [['A', 'B'], ['C', 'B'], ['A', 'B', 'C', 'D']] names = ['A', 'B', 'C', 'D'] occurrences = OrderedDict((name, OrderedDict((name, 0) for name in names)) for name in names) # Find the co-occurrences: for l in document: for i in range(len(l)): for item in l[:i] + l[i + 1:]: occurrences[l[i]][item] += 1 # Print the matrix: print(' ', ' '.join(occurrences.keys())) for name, values in occurrences.items(): print(name, ' '.join(str(i) for i in values.values())) 

Salida;

  ABCD A 0 2 1 1 B 2 0 2 1 C 1 2 0 1 D 1 1 1 0 

Aquí hay otra solución que usa itertools y la clase Counter del módulo de collections .

 import numpy import itertools from collections import Counter document =[['A', 'B'], ['C', 'B'],['A', 'B', 'C', 'D']] # Get all of the unique entries you have varnames = tuple(sorted(set(itertools.chain(*document)))) # Get a list of all of the combinations you have expanded = [tuple(itertools.combinations(d, 2)) for d in document] expanded = itertools.chain(*expanded) # Sort the combinations so that A,B and B,A are treated the same expanded = [tuple(sorted(d)) for d in expanded] # count the combinations c = Counter(expanded) # Create the table table = numpy.zeros((len(varnames),len(varnames)), dtype=int) for i, v1 in enumerate(varnames): for j, v2 in enumerate(varnames[i:]): j = j + i table[i, j] = c[v1, v2] table[j, i] = c[v1, v2] # Display the output for row in table: print(row) 

La salida (que se puede convertir fácilmente en un DataFrame) es:

 [0 2 1 1] [2 0 2 1] [1 2 0 1] [1 1 1 0] 

Otra opción es usar el constructor csr_matrix((data, (row_ind, col_ind)), [shape=(M, N)]) de scipy.sparse.csr_matrix donde data , row_ind y col_ind satisfacen la relación a[row_ind[k], col_ind[k]] = data[k] .

El truco es generar row_ind y col_ind iterando sobre los documentos y creando una lista de tuplas (doc_id, word_id). data serían simplemente un vector de unos de la misma longitud.

Multiplicar la matriz docs-words por su transposición le daría la matriz de co-ocurrencias.

Además, esto es eficiente en términos de tiempo de ejecución y uso de memoria, por lo que también debe manejar grandes cuerpos.

 import numpy as np import itertools from scipy.sparse import csr_matrix def create_co_occurences_matrix(allowed_words, documents): print(f"allowed_words:\n{allowed_words}") print(f"documents:\n{documents}") word_to_id = dict(zip(allowed_words, range(len(allowed_words)))) documents_as_ids = [np.sort([word_to_id[w] for w in doc if w in word_to_id]).astype('uint32') for doc in documents] row_ind, col_ind = zip(*itertools.chain(*[[(i, w) for w in doc] for i, doc in enumerate(documents_as_ids)])) data = np.ones(len(row_ind), dtype='uint32') # use unsigned int for better memory utilization max_word_id = max(itertools.chain(*documents_as_ids)) + 1 docs_words_matrix = csr_matrix((data, (row_ind, col_ind)), shape=(len(documents_as_ids), max_word_id)) # efficient arithmetic operations with CSR * CSR words_cooc_matrix = docs_words_matrix.T * docs_words_matrix # multiplying docs_words_matrix with its transpose matrix would generate the co-occurences matrix words_cooc_matrix.setdiag(0) print(f"words_cooc_matrix:\n{words_cooc_matrix.todense()}") return words_cooc_matrix, word_to_id 

Ejecute el ejemplo:

 allowed_words = ['A', 'B', 'C', 'D'] documents = [['A', 'B'], ['C', 'B', 'K'],['A', 'B', 'C', 'D', 'Z']] words_cooc_matrix, word_to_id = create_co_occurences_matrix(allowed_words, documents) 

Salida:

 allowed_words: ['A', 'B', 'C', 'D'] documents: [['A', 'B'], ['C', 'B', 'K'], ['A', 'B', 'C', 'D', 'Z']] words_cooc_matrix: [[0 2 1 1] [2 0 2 1] [1 2 0 1] [1 1 1 0]] 

También puede utilizar trucos de matriz para encontrar la matriz de co-ocurrencia también. Espero que esto funcione bien cuando tengas un vocabulario más grande.

 import scipy.sparse as sp voc2id = dict(zip(names, range(len(names)))) rows, cols, vals = [], [], [] for r, d in enumerate(document): for e in d: if voc2id.get(e) is not None: rows.append(r) cols.append(voc2id[e]) vals.append(1) X = sp.csr_matrix((vals, (rows, cols))) 

Ahora, puedes encontrar la matriz de coocurrencia simplemente multiplicando XT con X

 Xc = (XT * X) # coocurrence matrix Xc.setdiag(0) print(Xc.toarray())