Stichwortverzeichnis aus Text erstellen

  • C#

Es gibt 11 Antworten in diesem Thema. Der letzte Beitrag () ist von mikeb69.

    Stichwortverzeichnis aus Text erstellen

    Hallo,

    Wir haben viele Dokumente in einem Ordner liegen.
    Um nun das Suchen des richtigen Dokumemts dem Anwender leichter zu machen wäre eine Stichwortliste gut.

    Hat jemand einen Ansatz wie ich hier an Besten vorgehen kann ?
    Wie findet man die prägnantesten Wörter eines Textes ?

    Gruss

    mikeb69
    Rein logisch würde ich Überschriften bevorzugen und dann Wörter zählen.
    Wort in Überschift zählt 10
    Wort in Text zählt 1
    There is no CLOUD - just other people's computers

    Q: Why do JAVA developers wear glasses?
    A: Because they can't C#

    Daily prayer:
    "Dear Lord, grand me the strength not to kill any stupid people today and please grant me the ability to punch them in the face over standard TCP/IP."
    Ich habe es einmal wie folgt umgesetzt:

    Tabelle:
    ID
    Dokumenten-Name
    Schlagwort

    id und Name sind selbsterklärend.
    Bei Schlagwort habe ich (ohne irgendwelche Trennzeichen) alle Wörter mit:
    Überschriften-Charakter, Fett, Kursiv, Unterstrichen, <whatever-U-want>
    eingetragen.

    Dann brauchst du zum abgleich nur where schlagwort like '%<search>%' abfragen und erhälst die Treffermenge. (-> bei einem Stichwort, ansonsten Suchtext splitten und einzeln suchen, ggf. nur Schnittmenge anzeigen)

    Bei mir hat es sich erstmal um reines HTML gehandelt, weshalb du je nach Dokumententyp schauen musst wie du Fett, Kursiv, etc. erkennst und extrahierst.
    Achja und ne Blacklist an Worten ist auch Sinnvoll, weil 200x "DER/DIE/DAS" bringt kein Ergebnis.


    LG Acr0most
    Wenn das Leben wirklich nur aus Nullen und Einsen besteht, dann laufen sicherlich genügen Nullen frei herum. :D
    Signature-Move 8o
    kein Problem mit privaten Konversationen zu Thema XY :thumbup:
    @mikeb69 Nimm nur groß geschriebene Worte.
    Nimm nicht Wörter, die nur aus großen Buchstaben bestehen.
    Nimm nur Wörter ab einer bestimmten Länge (musst Du testen).
    Schmeiß Dubletten raus (Eisenbahn - Eisenbahnen, ...).
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hallo,

    vielen Dank für den Input.
    Auf einige der vorgeschlagenen Punkte bin ich auch selbst gekommen, interessant ist der Tipp von @RodFromGermany bezüglich der Dubletten und der groß geschriebenen Wörter.

    Gruss

    mikeb69
    Hallo,

    hab mal eine Klasse hierzu gebastelt.

    C#-Quellcode

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. namespace WindowsFormsApplication3
    5. {
    6. public class CreateTagList
    7. {
    8. /// <summary>
    9. /// Neue Instanz
    10. /// </summary>
    11. /// <param name="textToDecode">Text aus dem die Schlüsselworte (Tags) herausgefiltert werden sollen</param>
    12. /// <param name="badWords">Liste von Wörtern die keine Stichworte (Tags) sein können</param>
    13. public CreateTagList(string textToDecode, IEnumerable<string> badWords)
    14. {
    15. this.TextToDecode = textToDecode;
    16. this.BadWords = badWords;
    17. }
    18. /// <summary>
    19. /// Tag-Liste erstellen
    20. /// </summary>
    21. /// <param name="Result">Rückgabe des Ergebnisses</param>
    22. /// <returns>Anzahl der Tags</returns>
    23. public int FindTags(out IEnumerable<string> Result )
    24. {
    25. // String Splitten
    26. string [] tempAfterSplitting = this.TextToDecode.Split(new string[] { " ", ".", ",", ";", ":", Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries);
    27. // Alle Elemente entfernen die länger als 4 sind
    28. this.Tags = tempAfterSplitting.Where(x => x.Length > 4 && Char.IsUpper(x[0]));
    29. // Alle doppelten Elemente entfernen
    30. this.Tags = this.Tags.Union(this.Tags);
    31. List<string> result = new List<string>();
    32. if (this.BadWords != null)
    33. {
    34. // Liste nach 'verbotenen' Wörtern filtern
    35. foreach (string s in this.Tags)
    36. {
    37. if (this.BadWords.FirstOrDefault(x => x.Equals(s, StringComparison.OrdinalIgnoreCase)) == null)
    38. {
    39. result.Add(s);
    40. }
    41. }
    42. }
    43. // Ähnliche Wörter ausblenden
    44. // Ergebnis ausgeben
    45. this.Tags = result;
    46. // Weitere Ausgabe des Ergebnisses
    47. Result = this.Tags;
    48. return Result.Count();
    49. }
    50. /// <summary>
    51. /// Text aus dem die Schlüsselworte (Tags) herausgefiltert werden sollen
    52. /// </summary>
    53. public string TextToDecode { get; private set; }
    54. /// <summary>
    55. /// Rückgabe des Ergebnisses
    56. /// </summary>
    57. public IEnumerable<string> Tags { get; private set; }
    58. /// <summary>
    59. /// Liste von Wörtern die keine Stichworte (Tags) sein können
    60. /// </summary>
    61. public IEnumerable<string> BadWords { get; private set; }
    62. }
    63. }

    Als Text hab ich einfach einen Zeitungsartikel genommen.

    Was noch fehlt ist das mit den ähnlichen Wörtern.
    Hier läuft mir immer der Begriff 'Stemmer' über den Weg ?!?

    Edit:
    oder besser dieser Weg ?

    "http://stackoverflow.com/a/26049961" schrieb:

    The simplest code would involve regular expressions.

    For example, this would identify some English suffixes:

    '^(.*?)(ing|ly|ed|ious|ies|ive|es|s|ment)?$'One problem is that stemming is not as accurate as lemmatization. Lematization would require POS tagging for accuracy. For example, you don't want to add an -ing suffix to dove if it's a noun.

    Another problem is that some suffixes also require prefixes. For example, you must add en- to -rich- to add a -ment suffix in en-rich-ment -- unlike a root like -govern- where you can add the suffix without any prefix.



    Gruss

    mikeb69

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „mikeb69“ ()

    mikeb69 schrieb:

    Was noch fehlt ist das mit den gleichen Wörtern.


    Idee: Vor dem result.Add(s) per result.Contains(s); überprüfen, ob das Wort bereits vorhanden ist.

    LG Acr0most
    Wenn das Leben wirklich nur aus Nullen und Einsen besteht, dann laufen sicherlich genügen Nullen frei herum. :D
    Signature-Move 8o
    kein Problem mit privaten Konversationen zu Thema XY :thumbup:
    @Acr0most

    sorry - hab mich da verschrieben.

    Es fehlt mir das mit den ähnlichen Wörtern - @RodFromGermany nannte diese Dubletten
    [Schmeiß Dubletten raus (Eisenbahn - Eisenbahnen, ...).


    Die gleichen Wörter schmeiß ich mit dem Code raus

    C#-Quellcode

    1. // Alle doppelten Elemente entfernen
    2. this.Tags = this.Tags.Union(this.Tags);

    Gruss

    mikeb69
    @mikeb69 Dazu fällt mir die Levenshtein-Distanz ein.
    25 Treffer im Forum.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @RodFromGermany

    hab das hier gebastelt ;) und für den Sourcecode Austausch angemeldet.

    C#-Quellcode

    1. ​public class CreateTagList
    2. {
    3. /// <summary>
    4. /// Neue Instanz
    5. /// </summary>
    6. /// <param name="textToDecode">Text aus dem die Schlüsselworte (Tags) herausgefiltert werden sollen</param>
    7. /// <param name="badWords">Liste von Wörtern die keine Stichworte (Tags) sein können</param>
    8. public CreateTagList(string textToDecode, IEnumerable<string> badWords)
    9. {
    10. this.TextToDecode = textToDecode;
    11. this.BadWords = badWords;
    12. }
    13. /// <summary>
    14. /// Tag-Liste erstellen
    15. /// </summary>
    16. /// <param name="Result">Rückgabe des Ergebnisses</param>
    17. /// <returns>Anzahl der Tags</returns>
    18. public int FindTags(out IEnumerable<string> Result )
    19. {
    20. // String Splitten
    21. string [] tempAfterSplitting = this.TextToDecode.Split(new string[] { " ", ".", ",", ";", ";", Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries);
    22. // Alle Elemente entfernen die länger als 4 sind
    23. this.Tags = tempAfterSplitting.Where(x => x.Length > 4 && Char.IsUpper(x[0]));
    24. // Alle doppelten Elemente entfernen
    25. this.Tags = this.Tags.Union(this.Tags);
    26. List<string> result = new List<string>();
    27. if (this.BadWords != null)
    28. {
    29. // Liste nach 'verbotenen' Wörtern filtern
    30. foreach (string s in this.Tags)
    31. {
    32. if (this.BadWords.FirstOrDefault(x => x.Equals(s, StringComparison.OrdinalIgnoreCase)) == null)
    33. {
    34. result.Add(s);
    35. }
    36. }
    37. }
    38. // Ähnliche Wörter ausblenden
    39. for(int i = result.Count() -1; i > 0; i--)
    40. {
    41. IEnumerable<string> ret = this.Tags.Where(x => x.LevenshteinDistance(result[i]) <= 2);
    42. if (ret.Count() > 1)
    43. {
    44. // Wort doppelt vorhanden
    45. result.Remove(result[i]);
    46. }
    47. }
    48. // Ergebnis ausgeben
    49. this.Tags = result;
    50. // Weitere Ausgabe des Ergebnisses
    51. Result = this.Tags;
    52. return Result.Count();
    53. }
    54. /// <summary>
    55. /// Text aus dem die Schlüsselworte (Tags) herausgefiltert werden sollen
    56. /// </summary>
    57. public string TextToDecode { get; private set; }
    58. /// <summary>
    59. /// Rückgabe des Ergebnisses
    60. /// </summary>
    61. public IEnumerable<string> Tags { get; private set; }
    62. /// <summary>
    63. /// Liste von Wörtern die keine Stichworte (Tags) sein können
    64. /// </summary>
    65. public IEnumerable<string> BadWords { get; private set; }
    66. }
    67. public static class Levenshtein
    68. {
    69. public static int LevenshteinDistance(this string source, string target)
    70. {
    71. if (String.IsNullOrEmpty(source))
    72. {
    73. if (String.IsNullOrEmpty(target)) return 0;
    74. return target.Length;
    75. }
    76. if (String.IsNullOrEmpty(target)) return source.Length;
    77. if (source.Length > target.Length)
    78. {
    79. var temp = target;
    80. target = source;
    81. source = temp;
    82. }
    83. var m = target.Length;
    84. var n = source.Length;
    85. var distance = new int[2, m + 1];
    86. // Initialize the distance 'matrix'
    87. for (var j = 1; j <= m; j++) distance[0, j] = j;
    88. var currentRow = 0;
    89. for (var i = 1; i <= n; ++i)
    90. {
    91. currentRow = i & 1;
    92. distance[currentRow, 0] = i;
    93. var previousRow = currentRow ^ 1;
    94. for (var j = 1; j <= m; j++)
    95. {
    96. var cost = (target[j - 1] == source[i - 1] ? 0 : 1);
    97. distance[currentRow, j] = Math.Min(Math.Min(
    98. distance[previousRow, j] + 1,
    99. distance[currentRow, j - 1] + 1),
    100. distance[previousRow, j - 1] + cost);
    101. }
    102. }
    103. return distance[currentRow, m];
    104. }
    105. }


    Quelle für die LevenshteinDistanz:
    https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance

    ​Hab ich was übersehen ?

    Gruss

    mikeb69
    @mikeb69 Hast Du das getestet? Ich hab einmal Deinen Quellcode, einmal einen Brief vorgegeben.
    Bemerkungen:
    Splitte auch an "\t", "\n" (Word hat so was).
    Wenn this.BadWords == null ist, kommt kein Ergebnis raus. ;(
    Wäre es nicht besser, die Berechnungsfunktion mit solch Signatur zu versehen:

    C#-Quellcode

    1. public IEnumerable<string> FindTags()
    ====

    C#-Quellcode

    1. if (this.BadWords != null)
    2. {
    3. // ...
    4. }
    5. else
    6. {
    7. result.AddRange(this.Tags.ToArray());
    8. }

    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „RodFromGermany“ ()