3 # :Copyright: © 2014 Günter Milde.
4 # Released without warranty under the terms of the
5 # GNU General Public License (v. 2 or later)
8 # analyse.py: Sammeln und Sortieren von Teilwörtern
9 # =================================================
11 # Erstelle eine Liste der Teilwörter von in der Wortliste_ markierten
12 # zusammengesetzten Wörtern mit den Häufigkeiten des Auftretens als:
14 # :S: Einzelwort (Solitär)
15 # :E: erstes Wort in Verbindungen
16 # :M: mittleres Wort in Verbindungen
17 # :L: letztes Wort in Verbindungen
21 # * Teilwort mit Trennungen. Großschreibung wie Gesamtwort
22 # * Leerraum (whitespace)
23 # * Häufigkeiten in der Reihenfolge S;E;M;L
29 # .. _wortliste: ../../wortliste
36 # Importiere Python Module::
38 import re
# Funktionen und Klassen für reguläre Ausdrücke
39 import sys
# sys.exit() zum Abbruch vor Ende (für Testzwecke)
41 from collections
import defaultdict
# Wörterbuch mit Default
43 from werkzeug
import WordFile
, join_word
, udiff
, uebertrage
, TransferError
48 # Sammlung von `dictionaries` mit Info über Teilwörter.
50 # >>> from analyse import teilwoerter
52 # >>> words = teilwoerter()
56 class teilwoerter(object):
58 # Dictionary mit Listen möglicher Trennungen. Es kann unterschiedlche
59 # Trennungen eines identischen Teilworts geben, z.B. "Ba-se" (keine Säure)
60 # vs. "Base" (in Base=ball)::
64 # Häufigkeiten des Auftretens der Teilwörter::
66 S
= defaultdict(int) # Einzelwort (Solitär)
67 E
= defaultdict(int) # erstes Wort in Verbindungen
68 M
= defaultdict(int) # mittleres Wort in Verbindungen
69 L
= defaultdict(int) # letztes Wort in Verbindungen
71 # >>> words.S['na'] += 1
72 # >>> print words.S['na'], words.E['na'], words.M['na'], words.L['na']
77 # >>> words.add(u'ein<tra·gen')
78 # >>> words.add(u'ein·tra-gen')
79 # >>> words.add(u'un<klar')
80 # >>> words.add(u'un<.klar')
81 # >>> words.add(u'un-klar')
82 # >>> print words.trennvarianten
83 # {u'unklar': [u'un<klar', u'un-klar'], u'eintragen': [u'ein<tra-gen']}
91 # Ignoriere Spezialtrennungen:
92 if re
.search(r
'[\[{/\]}]', wort
):
94 # ungünstige Trennungen:
98 # Entferne/Ersetze Markierung
99 wort
= re
.sub(ur
'([-<=])\.+', ur
'\1', wort
) # =. <. -.
100 wort
= re
.sub(ur
'\.+', ur
'·', wort
)
101 # wort schon vorhanden?
102 if key
in self
.trennvarianten
:
103 # Abgleich der Trennmarker
104 eintrag
= self
.trennvarianten
[key
]
106 wort
= uebertrage(eintrag
[-1], wort
, upgrade
=False)
107 eintrag
[-1] = uebertrage(wort
, eintrag
[-1], upgrade
=False)
108 except TransferError
:
110 if wort
!= eintrag
[-1]:
111 self
.trennvarianten
[key
].append(wort
)
113 self
.trennvarianten
[key
] = [wort
]
115 # Iterator über alle trennvarianten: Rückgabewert ist ein String
117 # >>> print [word for word in words.woerter()]
118 # [u'un<klar', u'un-klar', u'ein<tra-gen']
123 for varianten
in self
.trennvarianten
.values():
124 for wort
in varianten
:
127 # Schreibe (Teil)wörter und Häufigkeiten in eine Datei `path`::
129 def write(self
, path
):
131 outfile
= codecs
.open(path
, 'w', encoding
='utf8')
132 header
= u
'# wort S;E;M;L (Solitär, erstes/mittleres/letztes Wort)\n'
133 outfile
.write(header
)
135 for key
in sorted(self
.trennvarianten
.keys()):
136 for wort
in self
.trennvarianten
[key
]:
137 line
= u
'%s %d;%d;%d;%d\n' % (wort
,
138 self
.S
[key
], self
.E
[key
], self
.M
[key
], self
.L
[key
])
142 # Funktion zum Einlesen der Teilwortdatei::
144 def read_teilwoerter(path
):
146 words
= teilwoerter()
148 for line
in open(path
):
149 if line
.startswith('#'):
151 line
= line
.decode('utf8')
153 wort
, flags
= line
.split()
157 # raise ValueError('cannot parse line '+line.encode('utf8'))
159 key
= join_word(wort
)
160 flags
= [int(n
) for n
in flags
.split(u
';')]
162 for kategorie
, n
in zip([words
.S
, words
.E
, words
.M
, words
.L
], flags
):
163 if n
> 0: # denn += 0 erzeugt "key" (`kategorie` ist defaultdict)
171 # =====================
173 # Hilfsfunktion: Erkenne (Nicht-)Teile wie ``/{ll/ll`` aus
174 # ``Fuß=ba[ll=/{ll/ll=l}]eh-re``::
176 # >>> from analyse import spezialbehandlung
177 # >>> print spezialbehandlung(u']er.be')
179 # >>> print spezialbehandlung(u'er[<b/b')
182 def spezialbehandlung(teil
):
183 if re
.search(ur
'[\[{/\]}]', teil
):
185 teil
= re
.sub(ur
'\[<(.+)/[^\]]+', ur
'\1', teil
) # [<b/b
186 teil
= re
.sub(ur
'\{([^/]*)[^}]*$', ur
'\1', teil
)
187 teil
= re
.sub(ur
'\[([^/]*)[^\]]*$', ur
'\1', teil
)
188 teil
= re
.sub(ur
'^(.)}', ur
'\1', teil
)
189 teil
= re
.sub(ur
'^(.)\]', ur
'\1', teil
)
190 teil
= re
.sub(ur
'^\]([^/]*$)', ur
'\1', teil
) # ]er.be -> er.be
194 # Zerlege Wörter der Wortliste (unter `path`). Gib eine "teilwoerter"-Instanz
195 # mit dictionaries mit getrennten Teilwörtern als key und deren Häufigkeiten
196 # an der entsprechenden Position als Wert zurück::
199 def analyse(path
='../../wortliste', sprachvariante
='de-1901',
200 unfertig
=False, halbfertig
=True):
202 wordfile
= WordFile(path
)
203 words
= teilwoerter()
205 for entry
in wordfile
:
207 # Wort mit Trennungen in Sprachvariante::
209 wort
= entry
.get(sprachvariante
)
210 if wort
is None: # Wort existiert nicht in der Sprachvariante
213 # Teilwörter suchen::
215 # Zerlegen, leere Teile (wegen Mehrfachtrennzeichen '==') weglassen,
216 # "halbe" Spezialtrennungen entfernen:
217 teile
= [spezialbehandlung(teil
) for teil
in wort
.split(u
'=')
222 if u
'·' not in wort
or unfertig
:
223 # skip unkategorisiert, könnte Kopositum sein
225 words
.S
[join_word(wort
)] += 1
228 gross
= wort
[0].istitle()
231 if (halbfertig
or u
'·' not in teile
[0]
232 ) and not teile
[0].endswith(u
'<'): # Präfix wie un<=wahr=schein-lich
234 words
.E
[join_word(teile
[0])] += 1
238 if (halbfertig
or u
'·' not in teil
239 ) and not teil
.startswith(u
'>'): # Suffixe wie an-dert=halb=>fach)
240 if gross
: # Großschreibung übertragen
241 teil
= teil
[0].title() + teil
[1:]
243 words
.L
[join_word(teil
)] += 1
245 # mittlere Teilwörter
246 for teil
in teile
[1:-1]:
248 if not re
.search(ur
'[\[{].*[\]}]', teil
):
250 if (not(halbfertig
) and u
'·' in teil
# unkategorisiert
251 ) or teil
.endswith(u
'<'): # Präfix wie un<=wahr=schein-lich
253 if gross
: # Großschreibung übertragen
254 teil
= teil
[0].title() + teil
[1:]
256 words
.M
[join_word(teil
)] += 1
260 # Datenerhebung zum Stand der Präfixmarkierung in der Liste der Teilwörter::
262 def statistik_praefixe(teilwoerter
):
264 ausnahmen
= set(line
.decode('utf8').strip()
265 for line
in open('wortteile/vorsilbenausnahmen')
266 if not line
.startswith('#'))
268 # Präfixe (auch als Präfix verwendete Partikel, Adjektive, ...):
269 praefixe
= set(line
.rstrip().lower().decode('utf8')
270 for line
in open('wortteile/praefixe')
271 if not line
.startswith('#'))
273 markiert
= defaultdict(list) # mit < markierte Präfixe
274 kandidaten
= defaultdict(list) # (noch) mit - markierte Präfixe
275 unkategorisiert
= defaultdict(list) # mit · markierte Präfixe
276 # grundwoerter = defaultdict(int) # Wörter nach Abtrennen markierter Präfixe
277 ausnahmefaelle
= defaultdict(int)
280 for wort
in teilwoerter
.woerter():
281 # Abtrennen markierter Präfixe:
283 teile
= restwort
.split(u
'<')
284 for teil
in teile
[:-1]:
285 if teil
: # (leere Strings (wegen <<<<) weglassen)
286 markiert
[join_word(teil
.lower())].append(wort
)
288 # Abtrennen markierter Suffixe:
289 restwort
= restwort
.split(u
'>')[0]
290 # Silben des Grundworts
291 silben
= re
.split(u
'[-·.]+', restwort
)
292 silben
[0] = silben
[0].lower()
294 if (join_word(restwort
) in ausnahmen
295 or join_word(restwort
.split(u
'>')[0]) in ausnahmen
):
296 ausnahmefaelle
[silben
[0]] += 1
298 for i
in range(len(silben
)-1, 0, -1):
299 kandidat
= u
''.join(silben
[:i
])
300 if kandidat
.lower() in praefixe
:
301 # print i, kandidat, restwort, restwort[len(kandidat)+i-1]
302 if u
'>' == restwort
[len(kandidat
)+i
-1]:
303 ausnahmefaelle
[kandidat
] += 1
304 elif u
'·' in restwort
:
305 unkategorisiert
[kandidat
].append(wort
)
307 kandidaten
[kandidat
].append(wort
)
311 print (u
'\nPräfixe aus der Liste "wortteile/praefixe" und '
312 u
'gleiche Wortanfangssilben\nmarkiert mit:')
313 for vs
in sorted(praefixe
):
314 einzel
= (teilwoerter
.E
[vs
] + teilwoerter
.M
[vs
]
315 + teilwoerter
.E
[vs
.title()] + teilwoerter
.M
[vs
.title()])
316 print u
'%-10s %5d = %5d < %5d - %5d · %5d offen' % (vs
, einzel
,
317 len(markiert
[vs
]), ausnahmefaelle
[vs
],
318 len(unkategorisiert
[vs
]), len(kandidaten
[vs
])),
320 print u
':', u
' '.join(kandidaten
[vs
][:30]),
321 if len(kandidaten
[vs
]) > 30:
324 markiert
.pop(vs
, None)
326 print u
'Markierte Präfixe die nicht in der Präfix-Liste stehen:'
327 # markiert.pop('lang', 0) # von "ent<lang<<"
328 for vs
, i
in markiert
.items():
329 print vs
, u
' '.join(i
)
332 # Trennungsvarianten zum gleichen Key::
334 def mehrdeutigkeiten(words
):
335 for teil
in sorted(words
.trennvarianten
):
336 if len(words
.trennvarianten
[teil
]) == 1:
338 # Bekannte Mehrdeutigkeiten (meist engl./dt.):
339 if teil
in ('Anhalts', 'Base', 'George',
340 'herzog', # Her-zog/her>zog
341 'Mode', 'Made', 'Name',
342 'Page', 'Pole', 'Planes', 'Rate', 'Real',
343 'Spare', 'Station', 'Stations', 'Ville', 'Wales', 'Ware',
344 'griff' # gri[f-f/{ff/ff=f}]est
347 # Einzelwort und Präfix gleichlautend:
348 if len(words
.trennvarianten
[teil
]) == 2:
349 varianten
= [i
.rstrip(u
'<') for i
in words
.trennvarianten
[teil
]]
350 if varianten
[0] == varianten
[1]:
352 print teil
+ u
': ' + u
' '.join(words
.trennvarianten
[teil
])
355 # Bei Aufruf (aber nicht bei Import)::
357 if __name__
== '__main__':
359 # sys.stdout mit UTF8 encoding.
360 sys
.stdout
= codecs
.getwriter('UTF-8')(sys
.stdout
)
362 # erstelle/aktualisiere die Datei ``teilwoerter.txt`` mit den Häufigkeiten
363 # nicht zusammengesetzer Wörter als Einzelwort oder in erster, mittlerer,
364 # oder letzter Position in Wortverbindungen::
366 # sprachvariante = 'de-1901' # "traditionell"
367 sprachvariante
= 'de-1996' # Reformschreibung
368 # sprachvariante = 'de-1901-x-GROSS' # ohne ß (Schweiz oder GROSS)
369 # sprachvariante = 'de-1996-x-GROSS' # ohne ß (Schweiz oder GROSS)
370 # sprachvariante = 'de-CH-1901' # ohne ß (Schweiz) ("süssauer")
372 words
= analyse(sprachvariante
=sprachvariante
,
373 halbfertig
=True, unfertig
=True)
374 words
.write('teilwoerter-%s.txt'%sprachvariante
)
380 words
= read_teilwoerter(path
='teilwoerter-%s.txt'%sprachvariante
)
382 # Trennungsvarianten zum gleichen Key:
383 mehrdeutigkeiten(words
)
385 # Stand der Vorsilbenmarkierung:
386 statistik_praefixe(words
)