-
Notifications
You must be signed in to change notification settings - Fork 27
Expand file tree
/
Copy pathdat.py
More file actions
90 lines (72 loc) · 2.95 KB
/
Copy pathdat.py
File metadata and controls
90 lines (72 loc) · 2.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
"""Compute score for Divergent Association Task,
a quick and simple measure of creativity
(Copyright 2021 Jay Olson; see LICENSE)"""
import re
import itertools
import numpy
import scipy.spatial.distance
class Model:
"""Create model to compute DAT"""
def __init__(self, model="glove.840B.300d.txt", dictionary="words.txt", pattern="^[a-z][a-z-]*[a-z]$"):
"""Join model and words matching pattern in dictionary"""
# Keep unique words matching pattern from file
words = set()
with open(dictionary, "r", encoding="utf8") as f:
for line in f:
if re.match(pattern, line):
words.add(line.rstrip("\n"))
# Join words with model
vectors = {}
with open(model, "r", encoding="utf8") as f:
for line in f:
tokens = line.split(" ")
word = tokens[0]
if word in words:
vector = numpy.asarray(tokens[1:], "float32")
vectors[word] = vector
self.vectors = vectors
def validate(self, word):
"""Clean up word and find best candidate to use"""
# Strip unwanted characters
clean = re.sub(r"[^a-zA-Z- ]+", "", word).strip().lower()
if len(clean) <= 1:
return None # Word too short
# Generate candidates for possible compound words
# "valid" -> ["valid"]
# "cul de sac" -> ["cul-de-sac", "culdesac"]
# "top-hat" -> ["top-hat", "tophat"]
candidates = []
if " " in clean:
candidates.append(re.sub(r" +", "-", clean))
candidates.append(re.sub(r" +", "", clean))
else:
candidates.append(clean)
if "-" in clean:
candidates.append(re.sub(r"-+", "", clean))
for cand in candidates:
if cand in self.vectors:
return cand # Return first word that is in model
return None # Could not find valid word
def distance(self, word1, word2):
"""Compute cosine distance (0 to 2) between two words"""
return scipy.spatial.distance.cosine(self.vectors.get(word1), self.vectors.get(word2))
def dat(self, words, minimum=7):
"""Compute DAT score"""
# Keep only valid unique words
uniques = []
for word in words:
valid = self.validate(word)
if valid and valid not in uniques:
uniques.append(valid)
# Keep subset of words
if len(uniques) >= minimum:
subset = uniques[:minimum]
else:
return None # Not enough valid words
# Compute distances between each pair of words
distances = []
for word1, word2 in itertools.combinations(subset, 2):
dist = self.distance(word1, word2)
distances.append(dist)
# Compute the DAT score (average semantic distance multiplied by 100)
return (sum(distances) / len(distances)) * 100