Schrift
[thread]12143[/thread]

Probleme mit Referenzen und komplexen Datenstrukturen

Leser: 1


<< >> 9 Einträge, 1 Seite
Dingels
 2008-07-07 22:24
#111922 #111922
User since
2008-07-07
31 Artikel
BenutzerIn
[default_avatar]
Guten Abend allerseits,

ich bin der Computerlinguist, dem ihr vor einigen Wochen schon mal sehr geholfen habt. Noch mal ein großes Dankeschön. :-)

Nach einigen Aufgaben, die ich selbst lösen konnte, sitze ich diesmal seit Stunden vor einer Aufgabe und weiß nicht weiter. Es geht um die Verwendung von Referenzen und komplexen Datenstrukturen.

Wie das in der Theorie funktioniert, ist mir klar, aber in der Anwendung scheitert es. Ihr würdet mir sehr helfen, wenn ihr mir bei folgender Aufgabe helfen könntet. Ihr sollt nicht meine Hausaufgaben machen, aber zumindest einen Tipp, wie ich vorgehen sollte, könnt ihr doch bestimmt geben. Schon mal ein großes Danke dafür. :-)

Zur Aufgabe:
Ich habe ein Textkorpus mit annotierten Daten in folgender Form:

Quote
Ich PRO.Pers.Subst.1.Nom.Sg.*6 Ich
habe VFIN.Haben.1.Sg.Pres.Ind haben
einen ART.Indef.Acc.Sg.Masc eine
Menschen N.Reg.Acc.Sg.Masc Mensch
gesehen VPP.Full.Psp sehen
, SYM.Pun.Comma ,
welcher PRO.Inter.Subst.-3.Nom.Sg.Masc welche
schwankte VFIN.Full.1.Sg.Past.Ind schwanken
und CONJ.Coord.-2 und
umsank VFIN.Full.3.Sg.Past.Ind <unknown>
. SYM.Pun.Sent .


Links stehen die Wörter des Textes, in der Mitte das Tag (mit Infos für Kasus, Genus usw) und rechts das Lemma, also die Grundform des Wortes.

Die Aufgabe besteht darin, das Korpus so umzuformatieren, dass links die unterschiedlichen Worttypen (also nicht jedes einzelne Vorkommen eines Wortes) alphabetisch absteigend sortiert ausgegeben wird und rechts daneben die jeweiligen Tags, mit denen der Worttyp auftritt. Dabei sollen die Tags nach Häufigkeit sortiert werden. Das heißt, das häufigste Tag soll als erstes hinter dem Worttyp stehen, dann das zweithäufigste usw. Die Häufigkeiten können wir auch dahinterschreiben, sind aber nicht unbedingt notwendig, solange die Reihenfolge stimmt. Also so zB.:

Quote
ahnte VFIN.Full.3.Sg.Past.Ind (3) VFIN.Full.1.Sg.Past.Ind (2) VFIN.Full.1.Sg.Past.Subj (2)
als CONJ.Comp.-2 (196) CONJ.SubFin.-2 (61) APPR.Als(33) CONJ.Coord.Als(3)


Wir sollten uns dabei am Code eines Beispielprogramms unseres Dozenten orientieren, aber das hilft mir nicht wirklich weiter. Mein Code sieht bisher so aus (das Korpus ist schon in der Variablen $lexikon eingelesen):


Code (perl): (dl )
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
my @zeilen_lexikon = split ("\n", $lexikon);

my $token;
my $tag;
my $lemma;

my %hash;

foreach my $zeile (@zeilen_lexikon) {
        # Die Leerzeichen zwischen Worttypen und Tags normalisieren: 
        $zeile =~ s/\s+/ /g;
        
        # Worttyp, Tag und Lemma voneinander trennen:
        if ($zeile =~ /([A-ZÄÖÜa-zäöüß0-9,;.:!?"'\(\)\[\]\{\}\-]+)\s([A-Z]+(\.[A-Za-z0-9\-\*]+){1,6})\s([A-ZÄÖÜa-zäöüß0-9,;.:!?"'\(\)\[\]\{\}\-]+)/) {
                $token = $1;
                $tag = $2;
                $lemma =$4;     
                
                # Ich zähle die  verschiedenen Kombinationen aus Worttyp und Tag:
                $hash{lc($token)}->{$tag}++;
        }
}

# Ausgabe
foreach my $token (sort keys %hash) {
        foreach my $tag (sort {${$hash{$token}}{$b} <=> ${$hash{$token}}{$a}} keys %{$hash{$token}}) {
                print $hash{$token} . "\t\t" . $tag . "\t\t" . $hash{$token}->{$tag} . "\n";
        }
}


Mit diesem Code weiß ich jetzt wenigstens schon mal, welcher Worttyp mit welchem Tag wie oft auftritt. Das ergibt z.B. folgenden Output:

Quote
alle PRO.Indef.Attr.-3.Acc.Pl.Fem 12
alle PRO.Indef.Attr.-3.Acc.Pl.Masc 7
alle PRO.Indef.Attr.-3.Nom.Pl.Fem 7
alle PRO.Indef.Attr.-3.Acc.Sg.Fem 5
alle PRO.Indef.Attr.-3.Acc.Pl.Neut 2
alle PRO.Indef.Attr.-3.Acc.Pl.*6 1
alle PRO.Indef.Attr.-3.Nom.Pl.*6 1


Jetzt weiß ich aber nicht, wie ich diese Ergebnisse in die richtige Form bringen soll. Meine Idee war, dass ich einen Hash baue, in dem die Worttypen als Schlüssel und die Tags als Werte gespeichert werden. Dabei sollen die Tags in Listen, also Arrays abgelegt werden. Das heißt, die Arrays sollen als Werte der Schlüssel fungieren. Aber leider scheint das nicht zu funktionieren. Ich hab schon zig Sachen ausprobiert, aber ich komme nicht zur Lösung.


Könnt ihr mir einen Hinweis geben? Vielen vielen Dank! :-)


Schönen Gruß,
Dingels
pktm
 2008-07-08 00:52
#111923 #111923
User since
2003-08-07
2921 Artikel
BenutzerIn
[Homepage]
user image
Na, du könntest dir einen Hash erstellen, bei dem die Vollwortform der Schlüssel ist. Darin hast du dann wiederum einen Hash, dessen Schlüssel die Tags sind, und dessen Werte die Frequenz der Tags bezeichnet.
Dann hättest du sowas wie:
Code: (dl )
1
2
3
4
5
6
7
my $href = {
'ahnte' => {
'VFIN.Full.3.Sg.Past.Ind' => 3,
'VFIN.Full.1.Sg.Past.Ind' => 2,
'VFIN.Full.1.Sg.Past.Subj' => 2,
}
};


Einen Hash der Form
Quote
'ahnte' => {
'VFIN.Full.3.Sg.Past.Ind' => 3,
'VFIN.Full.1.Sg.Past.Ind' => 2,
'VFIN.Full.1.Sg.Past.Subj' => 2,
}


kannst du dann entweder durchlaufen und in einer separaten Liste sortiert speichern, oder, wenn ich mich recht entsinne, eine Schwartz'sche Transformation anwenden. Dazu mal der erste Suchmaschinen-Treffer: http://perl-seiten.homepage.t-online.de/html/perl_...

ich bin mir sicher auch mal irgendwo dazu en Perl-Modul gesehen zu haben.

Grüße, pktm
http://www.intergastro-service.de (mein erstes CMS :) )
Dingels
 2008-07-08 02:33
#111925 #111925
User since
2008-07-07
31 Artikel
BenutzerIn
[default_avatar]
@pktm

Vielen Dank für deine Antwort. :-)

Diese Hash-Form habe ich mit meinem oberen Code bereits erstellt. Durch die Art, wie ich die Kombinationen aus Wort und Tag mittels dem Hash zähle, wurde diese Struktur bereits angelegt, wie du sie in deinem ersten Code-Beispiel dargestellt hast. Das habe ich auch mittels des Data::Dumper-Moduls überprüft.

Wir haben beide denselben Gedanken, ich will aus dem inneren Hash ein nach den numeralen Werten absteigend sortiertes Array machen, aber genau an diesem Punkt komm ich nicht weiter. Wie macht man aus sowas ein Array? Sowas haben wir glaube ich gar nicht im Kurs behandelt!?

Das mit der Schwarz'schen Transformation versteh ich größtenteils, aber dieses Verfahren übernimmt ja nur die Sortierung, wenn ich das richtig verstehe. Das heißt, das Array muss vorher schon bestehen.

Wie kann ich daraus ein Array machen? Hat jemand einen leicht verständlichen Ansatz? Vielen Dank und gute Nacht.

Dingels
pktm
 2008-07-08 04:15
#111928 #111928
User since
2003-08-07
2921 Artikel
BenutzerIn
[Homepage]
user image
Ahso, du willst aus einem Hash-Element der art
Code: (dl )
1
2
3
4
5
    'ahnte' => {
'VFIN.Full.3.Sg.Past.Ind' => 3,
'VFIN.Full.1.Sg.Past.Ind' => 2,
'VFIN.Full.1.Sg.Past.Subj' => 2,
}


die Form my $aref = ['ahnte', 'VFIN.Full.1.Sg.Past.Ind (2)',...]; machen?

Dann lauf durch alle Einträge im Hash und füge sie jeweils in das Array ein:
Code: (dl )
1
2
3
4
5
6
7
8
9
# ungetestet...
my @partial_result = ();
while(my ($wort, $tag_freq) = each $href) { #$href aus Beitrag 2
push @partial_result, $wort;
# und jetzt noch die ganzen Einträge im Hash abarbeiten
while( my ($tag, $freq) = each %{$tag_freq} ) {
push @partial_result, ($tag . ' (' . $freq . ')');
}
}


Ist es das, was du suchst?
http://www.intergastro-service.de (mein erstes CMS :) )
Dingels
 2008-07-08 13:35
#111932 #111932
User since
2008-07-07
31 Artikel
BenutzerIn
[default_avatar]
Hi pktm,

ich sitze gerade in der Uni und kann deinen Code erst heute abend testen. Ist bei deinem Code denn gewährleistet, dass die Wortform immer am Anfang des Arrays steht? Denn das ist zwingend notwendig.

Ich hatte zunächst an folgende Form gedacht:

Code: (dl )
1
2
3
%hash = ( 'ahnte' => ['VFIN.Full.3.Sg.Past.Ind (3)', 'VFIN.Full.1.Sg.Past.Ind (2)',  'VFIN.Full.1.Sg.Past.Subj (2)'],
'als' => [...]
)


Die Wortform als der jeweilige Schlüssel im Hash, die Tags und dazugehörige Häufigkeit nach Häufigkeit absteigend sortiert im Array, welches den jeweiligen Wert im Hash bildet.
Lässt sich sowas auch implementieren?


Ansonsten schon mal vielen Dank für deine Mühe. Ich teste den Code heute abend und melde mich dann noch mal.

Schönen Gruß,
Dingels
KurtZ
 2008-07-08 14:52
#111934 #111934
User since
2007-12-13
411 Artikel
BenutzerIn
[default_avatar]
EDIT: Hat sich erübrigt siehe http://board.perl-community.de/thread/12143/#MSG8



Dingels erlaube mir bitte die Anmerkung dass man dir beser helfen könnte wenn du dich auf das wesentliche Beschränken würdest, dann würden auch mehr leute deine Posts durchlesen und antworten.

In Kürze:
Wie lege ich ein Array als Value eines anderes Hashes ab? Mit Referenzen :
perldsc

Wie sortiere ich ein Array? Mit Sort : -f sort

An deiner Stelle würd ich mir erstmal das rumgefummel mit Schwartzian Transformation sparen, und ein Hash von Arrays von Arrays anlegen:
1. Das unterste Array hat immer 2 Elemente der Paare aus Klassifikation und Häufigkeit.
2. Das mittlere sammelt die Referenzen auf diese Paare (unsortiert).
3. Das oberste Hash weist jeder Wortform die Referenz auf auf die mittlere Paarliste.

Vor der Ausgabe musst du dann das mittlere Array nur noch sortieren.

Natürlich könntest du die Daten auch nur ein 2-Gliedriges Hash von Hashes ablegen, dann musst du aber vor jeder Ausgabe das untere Hash in ein Array von Arrays transformieren um es überhaupt sortieren zu können. Sowas macht die Schwartzian Transformation implizit, finde ich aber für einen Anfänger didaktisch nicht sinnvoll.

TMTOWTDYOG (there's more than one way to dig your own grave)
KurtZ
 2008-07-08 15:04
#111935 #111935
User since
2007-12-13
411 Artikel
BenutzerIn
[default_avatar]
EDIT: Hat sich erübrigt siehe http://board.perl-community.de/thread/12143/#MSG8


Beispiel unsortiertes Hash von Arrays von Arrays
Code (perl): (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$wortform_href = {
          'als' => [
                    [
                       'CONJ.SubFin.-2',
                       61
                     ],
                      [
                       'CONJ.Coord.Als',
                       3
                     ]
                     [
                       'APPR.Als',
                       33
                     ],
                     [
                       'CONJ.Comp.-2',
                       196
                     ],
                   ]
        };


NACHTRAG: Sorry ich merke gerade dass ein Hash von Hashes doch angesagt ist, weil du ja auch irgendwann zählen musst. Sonst müsstest du jedesmal das mittlere Array linear durchsuchen.

Aber ich hoffe die Idee wird klar, Hashes kann man nicht sortieren, deswegen musst du irgendwie vor der Ausgabe ein temporäres Array generieren.
TMTOWTDYOG (there's more than one way to dig your own grave)
KurtZ
 2008-07-08 15:46
#111937 #111937
User since
2007-12-13
411 Artikel
BenutzerIn
[default_avatar]
Also IMHO hast du das Problem im Wesentlcihen schon von Anfang an gelöst, deinen Code hab ich nur mal aufgeräumt und direkt testbar gemacht ... wo ist dein Problem ?

NACHTRAG: OK du hattest $token und $lemma verwechselt


Code (perl): (dl )
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
#!/usr/bin/perl

use strict;
use warnings;
use Data::Dumper;

my %tagcount;

while (my $line=<DATA>) {
        my ($token,$tag,$lemma)= split /\s+/,$line;
        $tagcount{lc($lemma)}->{$tag}++;
};

#print Dumper \%tagcount;


foreach my $lemma (sort keys %tagcount) {
        my @tags_sortedby_count =
                sort { $tagcount{$lemma}->{$b} <=> $tagcount{$lemma}->{$a} }
                        keys %{ $tagcount{$lemma} };
        # Ausgabe
        print "$lemma ";
        foreach my $tag ( @tags_sortedby_count ) {
                print  "\t", $tag, " (", $tagcount{$lemma}->{$tag}, ")";
        }
        print "\n";
 }



__DATA__
Ich PRO.Pers.Subst.1.Nom.Sg.*6 Ich
habe VFIN.Haben.1.Sg.Pres.Ind haben
einen ART.Indef.Acc.Sg.Masc eine
Menschen N.Reg.Acc.Sg.Masc Mensch
allen PRO.Indef.Attr.-3.Acc.Pl.Fem alle
allen PRO.Indef.Attr.-3.Acc.Pl.Masc alle
all PRO.Indef.Attr.-3.Nom.Pl.Fem alle
allen PRO.Indef.Attr.-3.Acc.Sg.Fem alle
all PRO.Indef.Attr.-3.Acc.Pl.Neut alle
all PRO.Indef.Attr.-3.Acc.Pl.*6 alle
all PRO.Indef.Attr.-3.Nom.Pl.*6 alle
all PRO.Indef.Attr.-3.Acc.Sg.Fem alle
all PRO.Indef.Attr.-3.Acc.Pl.Neut alle
all PRO.Indef.Attr.-3.Acc.Pl.*6 alle
all PRO.Indef.Attr.-3.Nom.Pl.*6 alle
all PRO.Indef.Attr.-3.Acc.Pl.*6 alle
all PRO.Indef.Attr.-3.Nom.Pl.*6 alle


AUSGABE:
Code: (dl )
1
2
3
4
5
alle 	PRO.Indef.Attr.-3.Nom.Pl.*6 (3)	PRO.Indef.Attr.-3.Acc.Pl.*6 (3)	PRO.Indef.Attr.-3.Acc.Pl.Neut (2)	PRO.Indef.Attr.-3.Acc.Sg.Fem (2)	PRO.Indef.Attr.-3.Nom.Pl.Fem (1)	PRO.Indef.Attr.-3.Acc.Pl.Fem (1)	PRO.Indef.Attr.-3.Acc.Pl.Masc (1)
eine ART.Indef.Acc.Sg.Masc (1)
haben VFIN.Haben.1.Sg.Pres.Ind (1)
ich PRO.Pers.Subst.1.Nom.Sg.*6 (1)
mensch N.Reg.Acc.Sg.Masc (1)
TMTOWTDYOG (there's more than one way to dig your own grave)
Dingels
 2008-07-09 01:07
#111962 #111962
User since
2008-07-07
31 Artikel
BenutzerIn
[default_avatar]
@KurtZ

Ich glaube, ich stand tatsächlich etwas auf dem Schlauch. Im Grunde hatte ich ja schon fast die Lösung. Ist ja doch irgendwie simpel. Aber momentan habe ich nur Stress, die Klausurphase beginnt bald. Da sieht man halt mal den Wald vor lauter Bäumen nicht mehr. ^^

Ich danke dir vielmals. :-)

Und dir natürlich auch, pktm.

Ab sofort werde ich so ne Aufgabe ein paar Tage ruhen lassen, bevor ich mich wieder dransetze. Dann komme ich wahrscheinlich auch selbst auf die Lösung. ^^

Also, besten Dank nochmals. Hut ab vor eurer Leistung.


Schönen Gruß,
Dingels
<< >> 9 Einträge, 1 Seite



View all threads created 2008-07-07 22:24.