Hi ich hab ein csv mit etwa 20 Spalten und 10000 Zeilen. WIe kann ich die max. Länge jeder Spalte ermitteln (in Linux)? das Beispiel: Hund;Katze;Maus Peter;Udo,Robert sollte dann zu folgendem Ergebnis führen 5;5;6
Python- oder Perlscript. Perl ist hier nicht so beliebt :-), aber da geht das mit einem Einzeiler:
1 | $ perl -e 'while(<>){chomp;@x=split(";");$i=0;foreach $v(@x){$l=length($v);$s[$i]=$l unless $s[$i]>$l;$i++};} END {print join(";", @s) . "\n"}' test.csv |
2 | 5;5;6 |
Ich habe mal das Komma in der zweiten Zeile deiner Testdaten in ein Semikolon geändert. Falls Interesse besteht, kann ich den Einzeiler natürlich auch nochmal aufhübschen und kommentieren. Ansonsten schreib's dir in Python. Geht sicher auch mit Shell-Bordmitteln, wird aber weder schneller noch schöner davon.
Gargamel schrieb: > ich hab ein csv mit etwa 20 Spalten und 10000 Zeilen. WIe kann ich die > max. Länge jeder Spalte ermitteln (in Linux)? > > das Beispiel: > Hund;Katze;Maus > Peter;Udo,Robert > > sollte dann zu folgendem Ergebnis führen > 5;5;6 Es ist nur ein Schnellschuß, scheint aber zu funktionieren, wenn ich Deine Beispieldaten korrigiere (zweiter Delimiter der zweiten Zeile):
1 | #!/usr/bin/env python
|
2 | import sys |
3 | import csv |
4 | from argparse import ArgumentParser |
5 | |
6 | |
7 | def main(): |
8 | parser = ArgumentParser(description='calculate column lengths') |
9 | parser.add_argument('filename', help='CSV file') |
10 | parser.add_argument('--delimiter', '-d', type=str, default=';', |
11 | help='the delimiter') |
12 | args = parser.parse_args() |
13 | |
14 | with open(args.filename, 'r') as ifh: |
15 | reader = csv.reader(ifh, delimiter=args.delimiter) |
16 | data = [[len(point) for point in row] for row in reader] |
17 | #
|
18 | data = list(map(max, zip(*data))) |
19 | writer = csv.writer(sys.stdout, delimiter=args.delimiter) |
20 | writer.writerow(data) |
21 | |
22 | |
23 | if __name__ == '__main__': |
24 | main() |
Eine Alternative wäre das Programm "csvstat" aus dem ebenfalls in Python geschriebenen Paket "csvkit". Das lohnt sich sehr, wenn man häufiger mit CSV-Dateien zu tun hat.
Jörg W. schrieb: > Perl ist hier nicht so beliebt :-), aber da geht das mit einem > Einzeiler: > $ perl -e 'while(<>){chomp;@x=split(";");$i=0;foreach > $v(@x){$l=length($v);$s[$i]=$l unless $s[$i]>$l;$i++};} END {print > join(";", @s) . "\n"}' test.csv > 5;5;6
1 | csv.writer(sys.stdout, delimiter=';').writerow(list(map(max, zip(*[[len(p) for p in r] for r in csv.reader(open(sys.argv[1], 'r'), delimiter=';')])))) |
> Falls Interesse besteht, kann ich den Einzeiler natürlich auch nochmal > aufhübschen und kommentieren. Ansonsten schreib's dir in Python. Als Oneliner in Python ist es weder kürzer noch lesbarer, fürchte ich. Aber Python hat einen kleinen Vorteil: wenn in einem Datenpunkt der Delimiter enthalten und er darum gequotet ist, fällt es nicht auf die Nase. Ich bin immer noch sehr häufig mit CSVs unterwegs und weiß daher: das manuell zu parsen oder zu schreiben endet immer böse. Für Perl gibt es daher im CPAN das Modul Text::CSV, das aber erst installiert werden muß. :)
Oneliner in Python sind aufgrund der Zwangs-Einrückungen sowieso stark limitiert. Daran versuche ich mich gar nicht erst. In Perl nehme ich sie gern für "Eintags-Probleme" (Sachen, die ich nur jetzt und hier brauche und dann erst im nächsten Jahrhundert wieder). Klar, wenn man CSVs hat, die nicht mehr so simpel sind, ist es immer besser, einen der vorgefertigten Module zu benutzen.
Jörg W. schrieb: > Perl ist hier nicht so beliebt :-), aber da geht das mit einem > Einzeiler: > $ perl -e 'while(<>){chomp;@x=split(";");$i=0;foreach > $v(@x){$l=length($v);$s[$i]=$l unless $s[$i]>$l;$i++};} END {print > join(";", @s) . "\n"}' test.csv > 5;5;6 vielen Dank, hat auf Anhieb geklappt. Nur hab ich gemerkt, dass manche Zeilen vermutlich mehr Spalten haben (also mehr ";") als eigentlich geplant. Wie kann ich das csv auf zwei oder mehrere Dateien aufteilen, sodass in ok.csv die Zeilen geschrieben werden, die genau 20 SPalten haben und in einer oder mehreren anderen Dateien die restlichen Zeilen?
Gargamel schrieb: > Jörg W. schrieb: >> Perl ist hier nicht so beliebt :-), aber da geht das mit einem >> Einzeiler: >> $ perl -e 'while(<>){chomp;@x=split(";");$i=0;foreach >> $v(@x){$l=length($v);$s[$i]=$l unless $s[$i]>$l;$i++};} END {print >> join(";", @s) . "\n"}' test.csv >> 5;5;6 > > vielen Dank, hat auf Anhieb geklappt. > Nur hab ich gemerkt, dass manche Zeilen vermutlich mehr Spalten haben > (also mehr ";") als eigentlich geplant. > Wie kann ich das csv auf zwei oder mehrere Dateien aufteilen, sodass in > ok.csv die Zeilen geschrieben werden, die genau 20 SPalten haben und in > einer oder mehreren anderen Dateien die restlichen Zeilen? ich hab grad bemekrt an was es liegt. Einige Zeilen haben ein Semicolon in einem Eintrag, dieser wird mit quotation marks abgekrenzt. z.B: Hund;Katze;Maus Peter;Udo;Robert 0815;4711;"24;7" wie kann ich das perl script insofern abändern, dass bei einem semicolon innerhalb quotation marks nicht eine neue Spalte gezählt wird?
Gargamel schrieb: > ich hab grad bemekrt an was es liegt. > Einige Zeilen haben ein Semicolon in einem Eintrag, dieser wird mit > quotation marks abgekrenzt. z.B: > > Hund;Katze;Maus > Peter;Udo;Robert > 0815;4711;"24;7" > > wie kann ich das perl script insofern abändern, dass bei einem semicolon > innerhalb quotation marks nicht eine neue Spalte gezählt wird? Indem Du mein Python-Skript benutzt, das kann damit umgehen. Oder indem Du Jörgs Perlskript so änderst, daß es das Perl-Modul Text::CSV benutzt, das Du allerdings aus dem CPAN installieren müßtest.
Karl Käfer schrieb: > Indem Du mein Python-Skript benutzt, das kann damit umgehen. hab ich grad gemacht, hat einwandfrei geklappt, besten Dank!
Gargamel schrieb: > Karl Käfer schrieb: >> Indem Du mein Python-Skript benutzt, das kann damit umgehen. > > hab ich grad gemacht, hat einwandfrei geklappt, besten Dank! Immer gerne. Freut mich sehr, daß es geklappt hat, lieben Dank für die Rückmeldung! ;)
Karl Käfer schrieb: > Indem Du mein Python-Skript benutzt, das kann damit umgehen. Das wäre jetzt für den Fall auch meine Empfehlung gewesen. ;-) So ein Einzeiler ist als "quick&dirty" praktikabel für genau den Zweck, für den er gezimmert wurden ist. Wenn da mehr Sonderfälle drin sind, dann macht man es besser richtig (und das wäre hier halt ein "offizieller" Modul, der CSV-Dateien handhabt). Persönlich mag ich CSV-Dateien allerdings überhaupt nicht, bei denen das Trennzeichen auch noch im Eintrag auftreten können soll. Für mich ist an der Stelle dann die Grenze der Nützlichkeit von CSV erreicht.
Jörg W. schrieb: > Das wäre jetzt für den Fall auch meine Empfehlung gewesen. ;-) Das ehrt und freut mich, lieben Dank! > So ein Einzeiler ist als "quick&dirty" praktikabel für genau den Zweck, > für den er gezimmert wurden ist. Wenn da mehr Sonderfälle drin sind, > dann macht man es besser richtig (und das wäre hier halt ein > "offizieller" Modul, der CSV-Dateien handhabt). Das wäre mit Perl und Text::CSV ja genauso, Python hat sein csv-Modul halt schon an Bord und beides ist auf so ziemlich jedem Linux installiert. Wenn ich mich Recht entsinne, wurde Python sogar mal im Filesystem Hierarchy Standard oder der Linux Standard Base erwähnt. > Persönlich mag ich CSV-Dateien allerdings überhaupt nicht, bei denen das > Trennzeichen auch noch im Eintrag auftreten können soll. Für mich ist an > der Stelle dann die Grenze der Nützlichkeit von CSV erreicht. Hast schon Recht. CSVs sind außerdem ziemlich teuer, weil beim Lesen immer Zeichen für Zeichen verglichen werden muß, und leider kann es auch mehrere Dimensionen nicht gut abbilden. Andererseits sind CSVs ein universelles Format zum Datenaustausch, kann zur Not auch von Hand mit etwas wie more(1) oder less(1) (oder csvlook aus dem Paket csvkit) gelesen oder im Fall von Fehlern repariert werden und ist obendrein gut komprimierbar. Da habe ich schon sehr viel Schlimmeres erlebt.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.