Multirow Ascii Tabelle erstellen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Smuun
User
Beiträge: 16
Registriert: Montag 12. Januar 2015, 13:43

Erstmal ein herzliches Hallo an alle.

Ich habe folgendes Problem.
Ich habe eine Tabelle im Ascii Format mit folgender Formatierung:

T X Y Z I
1 6 7 8 9
2 6 7 8 9
3 6 7 8 9
4 6 7 8 9
4 6 7 8 9
5 6 7 8 9
5 6 7 8 9

Diese Tabelle muss ich in eine Multirow umkonstruieren.
Es sollte dann folgendermassen aussehen:

T X Y Z I X Y Z I
1 6 7 8 9
2 6 7 8 9
3 6 7 8 9
4 6 7 8 9 6 7 8 9
5 6 7 8 9 6 7 8 9

Das heißt, wenn der Wert von T aus einer Zeile mit dem der nächsten übereinstimmt, sollen die X, Y, Z und I Werte in eine Zeile hintereinander geschrieben werde.
Ich komm jedoch grad auf keine Idee, wie ich das umsetzen kann.
Ich hoffe ihr könnt mir helfen!

Vielen Dank im Vorraus für Eure Mühe

Mit freundlichn Grüßen

Simon
BlackJack

@Smuun: Ein Stichwort wäre `itertools.groupby()`. Ansonsten müsstest Du Dir halt überlegen wie man das Schritt für Schritt jemandem beschreiben würde der ein bisschen dumm ist — dem Rechner halt.

Können denn maximal zwei aufeinanderfolgende Zeilen den gleichen T-Wert haben?
Sirius3
User
Beiträge: 17746
Registriert: Sonntag 21. Oktober 2012, 17:20

@Smuun: weil groupby nicht jedem gleich geläufig ist, hier mal das Beispiel:

Code: Alles auswählen

>>> k = [[1, 6, 7, 8, 9], [2, 6, 7, 8, 9], [3, 6, 7, 8, 9], [4, 6, 7, 8, 9], [4, 6, 7, 8, 9], [5, 6, 7, 8, 9], [5, 6, 7, 8, 9]]
>>> [list(itertools.chain((u,), *(w[1:] for w in v))) for u, v in itertools.groupby(k, lambda d: d[0])]
[[1, 6, 7, 8, 9], [2, 6, 7, 8, 9], [3, 6, 7, 8, 9], [4, 6, 7, 8, 9, 6, 7, 8, 9], [5, 6, 7, 8, 9, 6, 7, 8, 9]]
Smuun
User
Beiträge: 16
Registriert: Montag 12. Januar 2015, 13:43

Mein erster Ansatz war, per 'groupby' zu zählen, wie oft, der gleiche Wert T vorhanden ist, und dann eine Schleife zu schreiben, in der einzelne Array erstellt werde, und ich die danach wieder als Tabelle zusammenfügen kann.
Doch leider hängt es bei mir an der Umetzung diese Arrays zu erstellen und wie ich den Array dann mit den Daten erweitere die denselben T wert haben

Mein Code sieht bisher so aus:

import pandas as pd
import numpy as np
from itertools import groupby

Code: Alles auswählen

data = pd.read_table('/home/simon/test/test_spur.asc', header=None, sep="\s")
data.columns = columns=['X','Y','Z','S','I','R','T']
print(data)
t_value = np.array(data['T'])
x_value =np.array(data['X'])
y_value =np.array(data['Y'])
z_value =np.array(data['Z'])
i_value =np.array(data['I'])

def create_points():
    for i in range(len(t_value)):
        points=[t_value[i] + x_value[i] + y_value[i] + z_value[i] + i_value[i]]
    return(points)

create_points()
Smuun
User
Beiträge: 16
Registriert: Montag 12. Januar 2015, 13:43

@ Black Jack:

Es könne bis z 15 Zeilen den gleichen T-Wert haben...
BlackJack

@Smuun: Das ist eine nicht ganz unwichtige Randbedingung weil man dann ja die Kopfzeile gar nicht erstellen kann bevor man die kompletten Zeilen verarbeitet hat. Oder gibt es da eigentlich keine Kopfzeile in der Datei?

Dein Beispielcode ist komisch/ungünstig weil Du erst die ganzen Spalten in parallele Sequenzen aufteilst, nur um dann wider über einen Index `i` aus den parallelen Sequenzen die zusammengehörigen Werte zusammenfügst. Dann hätte man die gar nicht erst trennen müssen und wäre diesen unnötigen Index los. ``for i in range(len(sequence)):`` ist in Python ein „anti pattern”. Wenn man so eine Zeile schreibt, dann macht man fast sicher etwas falsch oder unnötig komisch/umständlich.

Kann man mit Pandas denn überhaupt Frames mit Zeilen unterschiedlicher Länge erstellen?
Smuun
User
Beiträge: 16
Registriert: Montag 12. Januar 2015, 13:43

@Black Jack:

Die Tabelle kommt ohne Header. Als Trennzeichen wird ein Leerzeichen benutzt ("\s")
Das muss ich zum abspeichern auch wieder benutzen, da das Modul für die weitere Bearbeitung (SPDLib) unbedingt Leerzeichen als Trenner haben will.

Das mit dem auftrenne in Sequenzen war so ne Idee von mir. Hab bis jetzt immer nur mit "R" gecoded. Bin grade dabei Python zu lernen. Aber dass mein Weg bis jetzt komisch war/ist, habe ich auch schon bemerkt :wink:

Ich werde es jetzt gleich mal mit itertools probieren und bei weiteren Problemen nochmals hier posten.

Auf jeden Fall finde ich euren Support genial! Wie schnell hier Leute drauf reagieren!!! Einfach super! :D
Smuun
User
Beiträge: 16
Registriert: Montag 12. Januar 2015, 13:43

Noch eine Frage: Ich hab jetzt die Tabelle anders eingelesen:

Code: Alles auswählen

data_table = open('/home/simon/test/test_spur.asc')
data=data_table.read()
Wie kann ich das nun machen, dass jeweils eine Zeile als Array gespeichert wird?
BlackJack

@Smuun: Dateiobjekte sind Iteratoren über die Zeilen. Zum Lesen von CSV-Dateien gibt es in der Standardbibliothek das `csv`-Modul, denn Du willst ja nicht nur die Zeilen haben, sondern auch die Zeilen noch mal in Felder aufgretrennt.

Der Datentyp mit dem Du dann operierst sind Listen und keine Arrays.

Dateien sollte man auch wieder schliessen. Üblicherweise verwendet man das öffnen zusammen mit der ``with``-Anweisung, dann wird die Datei auf jeden Fall geschlossen wenn der ``with``-Block verlassen wird, egal aus welchem Grund.
Smuun
User
Beiträge: 16
Registriert: Montag 12. Januar 2015, 13:43

Ich hätte da noch eine Frage.
Ich Lese meine ASCII Datei folgendermassen ein:

Code: Alles auswählen

l=[]
with open("/home/simon/test/test_spur.asc") as f:
    r = csv.reader(f, delimiter=",")
    for row in r:
       returns = [(row)]
       l.extend(returns)
Jetzt ist das problem, das er mir die entstandenen Listen folgendermassen ausgibt:

['4589329.514 5436784.859 689.017 51 88 1 215248.851582'], ['4589329.728 5436785.093 690.513 40 4 1 215248.851586']]

Durch die Anführungszeichen, kann ich die einzelnen Elemente nicht per Index ansprechen.
Kann mir jemand helfen wie ich diese Anführungszeichen weg bekomme?
Sirius3
User
Beiträge: 17746
Registriert: Sonntag 21. Oktober 2012, 17:20

@Smuun: wenn man ein Element an eine Liste anhängen will, nimmt man append und nicht extend!
Der delimiter ist offensichtlich nicht ','.
Smuun
User
Beiträge: 16
Registriert: Montag 12. Januar 2015, 13:43

Das einlesen hat jetzt super geklappt, und das sortieren funktioniuert auch. Nur schreibt er mir die Werte nach denen ich sortiert hab doppelt. Das sollte nicht sein.
Aber ich verstehe nicht so warum. Vielleicht hat nochmal jemand ne Idee:

Code sieht folgendermassen aus:

Code: Alles auswählen

l=[]
with open("/home/simon/test/test_spur.asc") as f:
    r = csv.reader(f, delimiter=" ")
    for row in r:
       returns = [(row)]
       l.extend(returns)
       
print(l)
Wenn ich die Liste mit .append anlege, macht mir Python noch mehr Klammern und groupby fuktioniert nicht.

Die Ausgangsliste sieht so aus:
[['4589319.747', '5436773.357', '685.837', '49', '106', '1', '215248.851500']]

Sortiert werden soll wie oben in der Beispieltabelle aber hier nach dem Wert an 7. Stelle.
Also [6].

Sortierung mache ich folgendermaßen:

Code: Alles auswählen

[list(itertools.chain((u,), *(w[1:] for w in v))) for u, v in itertools.groupby(l, lambda d: d[6])]
So erfolgt die Ausgabe so:

[['215248.851509', '5436774.450', '688.077', '55', '98', '1', '215248.851509'],
['215248.851513', '5436775.015', '687.611', '73' ,'81', '1', '215248.851513', '5436775.596', '683.714','57', '12', '2', '215248.851513']]

Aber sie sollte so aussehen:

[['215248.851509', '5436774.450', '688.077', '55', '98', '1'],
['215248.851513', '5436775.015', '687.611', '73' ,'81', '1', '5436775.596', '683.714','57', '12', '2']]


Auf jeden Fall schonmal vielen Dank!
Zuletzt geändert von Smuun am Dienstag 13. Januar 2015, 16:47, insgesamt 1-mal geändert.
BlackJack

@Smuun: Dieser Unsinn den Du da mit `extend()` und `returns` und den vielen selbstgeschriebenen Klammern machst hat den Effekt von einem einfachen `append()`. Wenn Du das nicht siehst, dann hast Du Listen, und `append()` und `extend()` nicht verstanden und solltest erst weitermachen bis Du verstanden hast was Du da machst. Das sieht irgendwie nach raten aus.

Wobei man den gleichen Effekt auch mit einem `list()` mit dem Reader-Objekt als Argument erreichen kann, also ohne eine Schleife schreiben zu müssen.

Und selbst das muss man nicht einmal machen weil das Reader-Objekt ja ein Iterartor über die einzelnen Datensätze ist und `groupby()` ein iterierbares Objekt als erstes Argument erwartet.

Die Zeile zum gruppieren müsstest Du auch *verstehen*, denn die ist für die Randbedingungen mit den ersten Beispieldaten und man muss die natürlich an die neue Situation anpassen. Du hast ja nur den Index für die Gruppierungsspalte geändert, aber nicht angepasst wie die Zeilen zusammengefasst werden.
Smuun
User
Beiträge: 16
Registriert: Montag 12. Januar 2015, 13:43

@BlackJack
Wie gesagt, hab ich bis jetzt immer mit "R" geschrieben. Mir fällt die Umstellung grade schwer.
Ich werd mal versuchen es ohne die Schleife hin zu bekommen.
Und mit Recht hast du festgestellt, dass ich die Groupby Zeile leider immer noch nicht ganz verstehe...
Danke dir für deine Hilfe. Wird jetzt eine lange Lesenacht bei mir...
Smuun
User
Beiträge: 16
Registriert: Montag 12. Januar 2015, 13:43

So. Hab jetzt das einlesen und die Gruppierung nochmals überarbeitet.

Gehts das noch effizienter her vom Code, oder kann man das so lassen?

Code: Alles auswählen

import itertools
import csv

with open('/home/simon/test/test_spur.asc', 'rb') as f:
    reader = csv.reader(f, delimiter=" ")
    l = list(reader)

data=[list(itertools.chain((u,), *(w[0:4] for w in v))) for u, v in itertools.groupby(l, lambda d: d[5])]

temp = open(r'/home/simon/test/test_spur_done.asc', 'w')
writer = csv.writer(temp, dialect='space')
for row in data:
    writer.writerow(row)
temp.close()
Für eine kurze Beurteilung wäre ich dankbar. Und nicht mit Kritik sparen! :D
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Auf den schnellen Blick (ohne die Mechanismen zu verstehen):

- Zeile 6 ist obsolet, da ``reader`` (würde ich anders nennen!) bereits iterierbar ist und somit *direkt* an ``groupby`` übergeben werden kann. Insbesondere bei *großen* Daten wird Dein Code damit deutlich schneller.

- Wieso nutzt Du beim schreibenden Zugriff auf eine Datei *nicht* ``with``? ;-)

- Noch effizienter wäre es, wenn Du ``data`` nicht als Liste, sondern Generator-Ausdruck umschreibst. Damit könntest Du Lesen, Verarbeiten und Schreiben effizient vereinen, weil immer nur *ein* Objekt gelesen, bearbeitet und sofort geschrieben wird.
Ich würde mir also drei Funktionen schreiben: Eine zum Lesen (erwartet einen Dateinamen), die einen Iterator von Zeilen zurückliefert (yield!). Eine zum Verarbeiten, die einen Iterator von Zeilen erwartet und einen Iterator von bearbeiteten Objekten zurückliefert. Und schlussendlich eine zum Schreiben, die als Eingabe einen Iterator von bearbeiteten Objekten erwartet und einen Dateinamen.
Diese kannst Du dann so kombinieren:

Code: Alles auswählen

dump(process_data(load("in.csv")), "out.csv")
Neben einer hübschen Gliederung ist Dein ``process_data`` damit unabhängig, *woher* die Daten stammen. Und auch it dem Ergebnis kannst Du andere oder zusätzliche Dinge anstellen, als nur das Schreiben. Dies erleichtert auch das Testen ungemein :-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
Antworten