Created
July 27, 2026 17:25
-
-
Save me-suzy/8c0d3e62a162f0ed77cb2019d5c5aba4 to your computer and use it in GitHub Desktop.
Analiza stil autori
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """ | |
| Analiza stilistica (stilometrie): captureaza "amprenta de scris" a fiecarui autor | |
| si ii diferentiaza. NU foloseste embeddings semantice (acelea prind subiectul); | |
| foloseste trasaturi de STIL, care raman constante indiferent despre ce scrie autorul. | |
| Trei familii de trasaturi, combinate: | |
| 1. N-grame de caractere -> ortografie, morfologie, forme de epoca | |
| 2. Cuvinte-unelte -> semnatura stilistica clasica (cum leaga frazele) | |
| 3. Trasaturi structurale -> lungimea frazelor, punctuatie, diversitate lexicala | |
| Instalare: | |
| pip install scikit-learn numpy | |
| """ | |
| import re | |
| import numpy as np | |
| from sklearn.feature_extraction.text import TfidfVectorizer | |
| from sklearn.metrics.pairwise import cosine_similarity | |
| from sklearn.preprocessing import StandardScaler | |
| # ---------------------------------------------------------------------- | |
| # DATE: pentru fiecare autor, fragmentele lui de text (din cartile scanate). | |
| # Aici doua stiluri contrastante pe teme suprapuse, ca demonstratie. | |
| # ---------------------------------------------------------------------- | |
| autori = { | |
| "Autor_1880": [ | |
| "Bolnavul, cuprins fiind de o mare fierbințeală și de o slăbiciune a mădularelor, se cade a fi așezat la odihnă într-o încăpere răcoroasă și ferită de lumină.", | |
| "Se cuvine, după cum învățătura cea veche ne arată, a-i da bolnavului fierturi ușoare de ierburi, spre a-i alina suferința și a-i întări inima.", | |
| "Rana, de va fi cuprinsă de putrejune și de materie urât mirositoare, se va spăla cu luare-aminte și se va oblojii cu prișnițe calde.", | |
| "Iar de va stărui boala și noaptea nu va putea bolnavul a se odihni, i se va da fiertură de tei cu miere, spre a-i aduce somnul cel dulce.", | |
| ], | |
| "Autor_1920": [ | |
| "Pacientul cu febră mare și astenie se așează în repaus, în cameră răcoroasă, fără lumină puternică.", | |
| "Se administrează ceaiuri ușoare de plante. Scop: calmarea și susținerea cordului.", | |
| "Rană infectată, cu puroi: se spală atent. Se aplică comprese calde.", | |
| "În caz de insomnie persistentă, se recomandă infuzie de tei cu miere, seara.", | |
| ], | |
| } | |
| # ---------------------------------------------------------------------- | |
| # 1. TRASATURI STRUCTURALE (calculate manual, per fragment) | |
| # ---------------------------------------------------------------------- | |
| def trasaturi_structurale(text): | |
| fraze = [f for f in re.split(r"[.!?]", text) if f.strip()] | |
| cuvinte = re.findall(r"\w+", text.lower()) | |
| n_cuv = max(len(cuvinte), 1) | |
| lung_fraze = [len(re.findall(r"\w+", f)) for f in fraze] or [0] | |
| return [ | |
| np.mean(lung_fraze), # lungimea medie a frazei | |
| np.std(lung_fraze), # cat de variabile sunt frazele | |
| len(set(cuvinte)) / n_cuv, # diversitate lexicala (type-token ratio) | |
| text.count(",") / n_cuv, # densitatea virgulelor | |
| (text.count(";") + text.count(":")) / n_cuv, # punctuatie complexa | |
| np.mean([len(c) for c in cuvinte]), # lungimea medie a cuvantului | |
| ] | |
| # ---------------------------------------------------------------------- | |
| # PREGATIRE | |
| # ---------------------------------------------------------------------- | |
| texte, etichete = [], [] | |
| for autor, fragmente in autori.items(): | |
| for f in fragmente: | |
| texte.append(f) | |
| etichete.append(autor) | |
| autori_unici = sorted(set(etichete)) | |
| # ---------------------------------------------------------------------- | |
| # 2. N-GRAME DE CARACTERE (ortografie + morfologie + forme arhaice) | |
| # ---------------------------------------------------------------------- | |
| vec_char = TfidfVectorizer(analyzer="char_wb", ngram_range=(2, 4), min_df=1) | |
| X_char = vec_char.fit_transform(texte).toarray() | |
| # ---------------------------------------------------------------------- | |
| # 3. CUVINTE-UNELTE (semnatura stilistica: cum leaga autorul ideile) | |
| # Cuvinte fara continut tematic - de aceea prind stilul, nu subiectul. | |
| # ---------------------------------------------------------------------- | |
| cuvinte_unelte = [ | |
| "si", "și", "a", "de", "la", "cu", "in", "în", "se", "va", "va", | |
| "care", "ce", "spre", "iar", "dar", "insa", "însă", "fiind", "cade", | |
| "cuvine", "dupa", "după", "cel", "cea", "va", "i", "il", "îl", "ne", | |
| ] | |
| vec_func = TfidfVectorizer(vocabulary=list(set(cuvinte_unelte)), analyzer="word") | |
| X_func = vec_func.fit_transform(texte).toarray() | |
| # ---------------------------------------------------------------------- | |
| # 4. STRUCTURA | |
| # ---------------------------------------------------------------------- | |
| X_struct = StandardScaler().fit_transform( | |
| np.array([trasaturi_structurale(t) for t in texte]) | |
| ) | |
| # ---------------------------------------------------------------------- | |
| # COMBINARE (cu ponderi - n-gramele si structura conteaza cel mai mult) | |
| # ---------------------------------------------------------------------- | |
| def normalizeaza(M): | |
| n = np.linalg.norm(M, axis=1, keepdims=True) | |
| n[n == 0] = 1 | |
| return M / n | |
| X = np.hstack([ | |
| normalizeaza(X_char) * 1.0, | |
| normalizeaza(X_func) * 0.8, | |
| normalizeaza(X_struct) * 1.0, | |
| ]) | |
| # ---------------------------------------------------------------------- | |
| # 5. AMPRENTA fiecarui autor = media fragmentelor lui | |
| # ---------------------------------------------------------------------- | |
| amprente = {} | |
| for a in autori_unici: | |
| idx = [i for i, e in enumerate(etichete) if e == a] | |
| amprente[a] = X[idx].mean(axis=0) | |
| # ---------------------------------------------------------------------- | |
| # 6. CAT DE DISTINCTI sunt autorii intre ei | |
| # ---------------------------------------------------------------------- | |
| print("=== SEPARAREA STILISTICA INTRE AUTORI ===") | |
| A = np.vstack([amprente[a] for a in autori_unici]) | |
| S = cosine_similarity(A) | |
| for i, a in enumerate(autori_unici): | |
| for j, b in enumerate(autori_unici): | |
| if i < j: | |
| dist = 1 - S[i, j] | |
| verdict = "stiluri distincte" if dist > 0.3 else "stiluri apropiate" | |
| print(f" {a} vs {b}: distanta {dist:.3f} ({verdict})") | |
| print() | |
| # ---------------------------------------------------------------------- | |
| # 7. PROFIL STILISTIC: ce deosebeste fiecare autor (masuri interpretabile) | |
| # ---------------------------------------------------------------------- | |
| print("=== PROFILUL FIECARUI AUTOR ===") | |
| for a in autori_unici: | |
| idx = [i for i, e in enumerate(etichete) if e == a] | |
| feats = np.array([trasaturi_structurale(texte[i]) for i in idx]) | |
| m = feats.mean(axis=0) | |
| print(f" {a}:") | |
| print(f" fraza medie: {m[0]:.1f} cuvinte") | |
| print(f" variatie fraze: {m[1]:.1f}") | |
| print(f" diversitate lexicala: {m[2]:.2f}") | |
| print(f" virgule/cuvant: {m[3]:.3f}") | |
| print() | |
| # ---------------------------------------------------------------------- | |
| # 8. ATRIBUIRE: un fragment necunoscut - al cui stil e? | |
| # ---------------------------------------------------------------------- | |
| def atribuie(text): | |
| xc = normalizeaza(vec_char.transform([text]).toarray()) | |
| xf = normalizeaza(vec_func.transform([text]).toarray()) * 0.8 | |
| xs = normalizeaza(StandardScaler().fit( | |
| np.array([trasaturi_structurale(t) for t in texte]) | |
| ).transform([trasaturi_structurale(text)])) | |
| x = np.hstack([xc, xf, xs])[0] | |
| print(f"Fragment necunoscut: „{text[:55]}...”") | |
| scoruri = [(a, cosine_similarity([x], [amprente[a]])[0, 0]) for a in autori_unici] | |
| for a, s in sorted(scoruri, key=lambda t: -t[1]): | |
| print(f" {a}: {s:.3f}") | |
| castigator = max(scoruri, key=lambda t: t[1])[0] | |
| print(f" -> atribuit lui {castigator}\n") | |
| print("=== ATRIBUIREA UNUI TEXT NECUNOSCUT ===") | |
| atribuie("Iar bolnavul, fiind cuprins de friguri și de mare slăbiciune, se cade a se odihni în tihnă.") | |
| atribuie("Febră, frisoane, oboseală. Repaus la pat. Lichide.") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment