Seite 2 von 5

Re: Dateinamen in jede Zeile schreiben

Verfasst: Mittwoch 19. November 2014, 23:38
von Sirius3
@kidchino: Kannst Du mal ein Beispiel zeigen? Wie sieht liste aus und was wird davon geschrieben, bzw. nicht geschrieben?

Re: Dateinamen in jede Zeile schreiben

Verfasst: Donnerstag 20. November 2014, 09:23
von kidchino
@Sirius3:
Hey,

ich bin mir nicht so ganz sicher wie ich es gut darstellen kann.
Hier ist erstmal eine rar mit den drei Input Datei und der einen Output Datei. Alles gekürzt und nur ein paar byte klein.
https://dl.dropboxusercontent.com/u/727 ... terung.rar

Falls ihr es nicht öffnen wollt, ist hier ein Screenshot, der natürlich nur halb gut die Formatierung widergibt.
https://dl.dropboxusercontent.com/u/7277609/test.jpg

Wenn ich die Beispiele irgendwie anders zeigen soll oder kann, sagt einfach bescheid.

Ich wäre Euch so dankbar wenn es klappt. Ich arbeite seit über zwei Jahren an diesen Daten und jetzt kommen endlich die letzten Schritte.

VG
Alex

Re: Dateinamen in jede Zeile schreiben

Verfasst: Donnerstag 20. November 2014, 11:07
von BlackJack
Wenn ich das unter Linux laufen lasse funktioniert es. Das scheint eindeutig das Problem zu sein das Windows Textdateien nicht 1:1 so speichert wie man sie schreibt, sondern die Zeilenende-Zeichen ”übersetzt”. Um das zu umgehen würde man die Datei im Binärmodus öffnen. Was aber bei Python 3 anscheinend nicht mehr geht weil das `csv`-Modul dort jetzt eine Textdatei verlangt. :(

Re: Dateinamen in jede Zeile schreiben

Verfasst: Donnerstag 20. November 2014, 12:06
von kidchino
@BlackJack:

Wenn ich Python2 installieren und nutzen würde, müsste ich dann viel am Skript ändern. Ich bin ja kein Spezi (habt ihr ja gemerkt ;) ), der das in 2 Min hinbekommt. Oder bleiben die Methoden-Befehle etc. die gleichen?
Kann ich Python2 und Python3 auch parallel laufen lassen?

Vielen Dank für Eure Hilfe
Alex

Re: Dateinamen in jede Zeile schreiben

Verfasst: Donnerstag 20. November 2014, 12:17
von BlackJack
@kidchino: Ich habe es unter Linux gerade mal ausprobiert, einzige Änderung war der zusätzliche Binärmodus bei den beiden `open()`-Aufrufen.

Re: Dateinamen in jede Zeile schreiben

Verfasst: Donnerstag 20. November 2014, 14:26
von kidchino
@BlackJack:

1000 Dank. Es läuft alles perfekt mit python 2.78. Viel Dank für die ganze Mühe und Hilfe.
Aber auch an Sirius3 und /me: Vielen Dank.

Vielleicht hilft diese Erkenntnis ja auch einem Dritten.
BlackJack hat geschrieben:Das scheint eindeutig das Problem zu sein das Windows Textdateien nicht 1:1 so speichert wie man sie schreibt, sondern die Zeilenende-Zeichen ”übersetzt”. Um das zu umgehen würde man die Datei im Binärmodus öffnen. Was aber bei Python 3 anscheinend nicht mehr geht weil das `csv`-Modul dort jetzt eine Textdatei verlangt.
VG
kid

Re: Dateinamen in jede Zeile schreiben

Verfasst: Mittwoch 26. November 2014, 09:10
von kidchino
Hi zusammen,

ich leider schon wieder.

Leider kommt ein Fehler wenn ich ein paar mehr Dateien nutze:

Code: Alles auswählen

Traceback (most recent call last):
  File "C:\Python_Skripte\paper_add_name.py", line 20, in <module>
    for row in reader:         #schleife für jede zeile
Error: field larger than field limit (131072)
Die csv Datei wird auch relativ schnell groß. Nach 40 Dateien ist sie schon 16MB groß. (Ist ja aber kein Problem, nur zur Info)
Nun habe ich ca. 1000 Dateien im ersten Durchlauf.

Ich bin gar nicht sicher, was hier zu klein ist. Kann man das Limit ggf. erhöhen?

Hier nochmal das gesamte Skript:

Code: Alles auswählen

# -*- coding: utf-8 -*-
#Das Skript schreibt alle Zeilen aus allen Dateien eines Ordner in eine großes Datei und hängt den Datennamen hinten dran (4 Zellen)
import csv, os, sys

# relevanter Pfad mit Dateien
pfad = 'C:/papererweiterung/'

# Pfad zu den txt/csv Dateien 'Pfad'
liste_dateiname = os.listdir(pfad)

liste = [] #erstellt eine leeres Array
gesamte_datei = open('C:/papererweiterung1/New400Liste.csv', "wb")  #öffne die gesamte Datei
for dateiname in liste_dateiname:   # für jedes Element der Variable path 
        
    with open(os.path.join(pfad, dateiname), "rb") as f: #öffnet die jeweilige datei
        reader = csv.reader(f, delimiter="\t") #liest die zeilen aus, tabstop getrennt
        next(reader) #überspringt jeweils die erste zeile 
        #new_datei = open(os.path.join(pfad, dateiname) + "_new","w")
        #print (liste) #testausgabe        
        for row in reader:         #schleife für jede zeile   
            row.append(dateiname)#schreibt in jede zeile hinten den vollen dateinamen dran
            #del row[58]
            compid, nummer, _, subname = dateiname.split("#") #"splittet" den namen mit der Trennung "#"
            row.extend([compid, nummer, subname])#setzt den geteilten Dateinamen in die letzten drei Spalten
            liste.append(row) #schreibt die zeilen in die liste
writer = csv.writer(gesamte_datei,  delimiter = "\t")#gibt die liste weiter an die gesamte datei
writer.writerows(liste)#Zgibt die liste weiter an die gesamte datei


VG Kid

Re: Dateinamen in jede Zeile schreiben

Verfasst: Mittwoch 26. November 2014, 09:28
von BlackJack
@kidchino: Dann müsstest Du Dir die Datei bei der der Fehler auftritt mal anschauen warum da ein Feld so gross ist. Kann es vielleicht sein, dass nicht alle Dateien das gleiche Trennzeichen verwenden? Oder das auch andere Dateien als CSV-Dateien in dem Verzeichnis liegen? Bei Windows tauchen ja zum Beispiel gerne mal so `Thumbs.db`-Dateien überall auf.

Bezüglich der Dateigrösse(n): Dann würde ich nicht alles in den Arbeitsspeicher lesen sondern statt die Zeilen in einer Liste zu sammeln, sie gleich in die Ausgabedatei schreiben.

Re: Dateinamen in jede Zeile schreiben

Verfasst: Mittwoch 26. November 2014, 09:58
von kidchino
Noch ein paar Infos:

Bei 340 Dateien mit insgesamt 55,6 MB läuft alles gut. Dann kommt eine ca. ebenso große CSV raus. Alles bestens
Die 341. Datei ist eine 1,4Mb große Datei. Nehme ich die dazu kommt der Fehler.
Lass ich aber die 341. aus und nehme die zwei darauffolgenden dazu (2x 900kb = 1.8mb) läuft das Skirpt wieder gut. Jetzt dachte ich, dass die Dateigröße der einzelnen Datei evtl. entscheidend ist, aber
wenn ich dann noch ein paar Dateien dazu nehmen, die kleiner sind als 1mb, kommt der Fehler trotzdem wieder.

Update:
@BlackJack:
Da ist tatsächlich eine Zeile in der 341. Datei dabei, die allein 154252 Zeichen hat. Kann man das umgehen bzw. das Limit erhöhen oder muss ich die Zeile löschen.
Wenn man es nicht umgehen könnte, wäre es ideal, dass das Skripte mir die Datei "printed", bei der der Fehler auftritt. Dann könnte ich die Dateien per Hand nachbearbeiten.
kidchino hat geschrieben:Bezüglich der Dateigrösse(n): Dann würde ich nicht alles in den Arbeitsspeicher lesen sondern statt die Zeilen in einer Liste zu sammeln, sie gleich in die Ausgabedatei schreiben.
Geht es denn wenn ich alle Inhalte in eine Datei kopieren?
Ich muss nachher alle Daten in eine MySQL-Datenbank laden und es ist für mich sehr viel einfacher, 10 große Dateien einzulesen also 20.000 kleine.


VG
kid

Re: Dateinamen in jede Zeile schreiben

Verfasst: Mittwoch 26. November 2014, 10:08
von kidchino
Hey,

ich habe jetzt beim CSV.reader

Code: Alles auswählen

quoting=csv.QUOTE_NONE
hinzugefügt.
Könnte das Probleme nachher bei MySQL geben? Sonst würde ich diese Extremfälle doch lieber rausnehmen und per Hand bearbeiten.

Re: Dateinamen in jede Zeile schreiben

Verfasst: Mittwoch 26. November 2014, 10:15
von BlackJack
@kidchino: Das das Problem nicht die gesamtdateigrösse ist sagt die Fehlermeldung doch eigentlich, da steht „field size limit” und nicht „file size limit”. ;-)

Es gibt im `csv`-Modul die `field_size_limit()`-Funktion um dieses Limit zu setzen (und abzufragen).

Die Nachfrage verstehe ich nicht‽ Im Moment kopierst Du doch alles in eine Ausgabedatei. Ob das geht, also bei der Weiterverarbeitung, musst Du doch wissen‽

Im moment sammelst Du alle Daten in einer Liste und schreibst die in eine Ausgabedatei wenn die Schleife durchlaufen und alle Eingabedateien eingelesen sind. Stattdessen könntest Du dort wo Du einen Datensatz an die Liste anhängst diesen Datensatz gleich in die Ausgabedatei schreiben und die Liste komplett weg lassen. Damit beschränkt sich der benötigte Arbeitsspeicher auf den Platz der für die Verarbeitung des grössten Einzeldatensatzes gebraucht wird, und nicht mehr auf die Grösse die alle Datensätze in allen Dateien zusammen verbrauchen.

Ob Optionen beim *reader* Probleme beim importieren in die Datenbank machen, kann Dir hier keiner sagen. Dazu müsste man ja wissen wie die Daten aussehen, was die Optionen dabei bewirken, und was die Datenbank erwartet.

Re: Dateinamen in jede Zeile schreiben

Verfasst: Mittwoch 26. November 2014, 11:37
von kidchino
@BlackJack:
BlackJack hat geschrieben:@kidchino: Das das Problem nicht die gesamtdateigrösse ist sagt die Fehlermeldung doch eigentlich, da steht „field size limit” und nicht „file size limit”. ;-)
Haha, stimmt :)
BlackJack hat geschrieben:Es gibt im `csv`-Modul die `field_size_limit()`-Funktion um dieses Limit zu setzen (und abzufragen).
Okay, das probiere ich gleich mal aus.
BlackJack hat geschrieben:Die Nachfrage verstehe ich nicht‽ Im Moment kopierst Du doch alles in eine Ausgabedatei. Ob das geht, also bei der Weiterverarbeitung, musst Du doch wissen‽
BlackJack hat geschrieben:Ob Optionen beim *reader* Probleme beim importieren in die Datenbank machen, kann Dir hier keiner sagen. Dazu müsste man ja wissen wie die Daten aussehen, was die Optionen dabei bewirken, und was die Datenbank erwartet.
Meine Frage war eigentlich diese. Wenn ich jetzt die "zu großen fields" in der Datei lasse, dann ergebe sich doch bestimmt auch Probleme bei MySQL. Excel kann damit nicht um, das CSV-Modul meckert auch. Da dachte ich es ist naheliegend, dass auch MySQL Probleme damit hat wenn die Felder >x sind. Formatiert ist die MySQL Tabelle noch nicht, also könnte ich einfach eine Feldgröße vonr >x zulassen, falls x nicht ein Standard-Wert ist der und bspw. MySQL auf gar keinen Fall mit Felder umgehen kann, die >x sind.
BlackJack hat geschrieben:Im moment sammelst Du alle Daten in einer Liste und schreibst die in eine Ausgabedatei wenn die Schleife durchlaufen und alle Eingabedateien eingelesen sind. Stattdessen könntest Du dort wo Du einen Datensatz an die Liste anhängst diesen Datensatz gleich in die Ausgabedatei schreiben und die Liste komplett weg lassen. Damit beschränkt sich der benötigte Arbeitsspeicher auf den Platz der für die Verarbeitung des grössten Einzeldatensatzes gebraucht wird, und nicht mehr auf die Grösse die alle Datensätze in allen Dateien zusammen verbrauchen.
Der Ansatz ist nachvollziehbar und das dachte ich auch schon mal. Aber es ist ja immer nicht so einfach, wenn man bis vor einem Monat noch keinen Kontakt mit Programmiersprachen (außer Excel Makro und das ist ja weit entfernt) hatte. Aber ich werde es gleich mal probieren.

Ich versuche es ja mal so zu programmieren, wenn eine Feld zu groß ist, die Datei übersprungen und der Dateiname ausgegeben wird. Das würde ich auf Nummer Sicher gehen und müsste halt ein bisschen per Hand machen. Leider weiß ich nicht wie ich die Feldgröße ermitteln kann. Es müsste ja so in die Richtung gehen len(row).

VG

Re: Dateinamen in jede Zeile schreiben

Verfasst: Mittwoch 26. November 2014, 12:21
von BlackJack
@kidchino: Ob sich mit zu grossen Feldern Probleme bei MySQL ergeben hängt von zwei Faktoren ab: 1. hat das Programm das zum Importieren der CSV-Datei in die Datenbank verwendet wird eventuell eine Grössenbeschränkung für einzelne Felder, und 2. ist der Datentyp in der entsprechenden Spalte geeignet um den grossen Wert (komplett) aufzunehmen. Hier ist auch noch die Frage interessant was beim Import mit Feldern passiert die grössere Daten enthalten als der Datentyp der Spalte erlaubt — führt das zu einem Fehler und einem Abbruch des Imports, oder gibt es eine Warnung. In letzterem Fall: Was passiert mit dem Datensatz? Wird der komplett ignoriert, oder wird der Überhang der zu grossen Felder einfach ”abgeschnitten”? Die Fragen solltest Du klären und entscheiden wie Du mit diesen Fällen umgehen möchtest.

Für Textfelder ist der grösste MySQL-Typ soweit ich weiss LONGTEXT und der kann bis zu 2 Gigabyte gross werden. Ein Feld vom Typ TEXT kann maximal 64 Kilobyte aufnehmen.

Kannst Du die maximale Feldgrösse nicht sinnvoll abschätzen? Was sind denn das für Daten? Kann da zum Beispiel tatsächlich ein Feld mit mehr als einem Megabyte an Inhalt vorkommen oder wäre das eine Grösse die man einfach mal setzen kann?

Um die maximale Feldgrösse zu bestimmen müsste man die CSV-Datei parsen, aber um das zu machen muss man die maximale Feldgrösse kennen. ;-)

Man könnte bei Dateien die zu grosse Felder enthalten die maximale Feldgrösse vom `csv`-Modul solange schrittweise erhöhen bis sich die Datei verarbeiten lässt um die maximale Feldgrösse zu ermitteln. Aber wie gesagt, es sollte möglich sein einen sinnvollen Wert zu schätzen wenn man weiss was das für Daten sind. Wie gross ist denn die grösste Einzeldatei?

Re: Dateinamen in jede Zeile schreiben

Verfasst: Mittwoch 26. November 2014, 13:05
von kidchino
BlackJack hat geschrieben:@kidchino: [...] Die Fragen solltest Du klären und entscheiden wie Du mit diesen Fällen umgehen möchtest.
Ich habe mich entschieden, diese Dateien erstmal außer vor zu lassen.
BlackJack hat geschrieben:Kannst Du die maximale Feldgrösse nicht sinnvoll abschätzen? Was sind denn das für Daten? Kann da zum Beispiel tatsächlich ein Feld mit mehr als einem Megabyte an Inhalt vorkommen oder wäre das eine Grösse die man einfach mal setzen kann?
BlackJack hat geschrieben:Man könnte bei Dateien die zu grosse Felder enthalten die maximale Feldgrösse vom `csv`-Modul solange schrittweise erhöhen bis sich die Datei verarbeiten lässt um die maximale Feldgrösse zu ermitteln. Aber wie gesagt, es sollte möglich sein einen sinnvollen Wert zu schätzen wenn man weiss was das für Daten sind. Wie gross ist denn die grösste Einzeldatei?
Das habe ich schrittweise gemacht. Liegt bei ca. 245000-247000 aber ich konnte noch nicht alle Daten prüfen. Die größte Datei ist 1.495kb groß.

Ist denn das "field" ein Element einer Zeile oder die gesamte Zeile an sich?

Hiermit:

Code: Alles auswählen

print(len(row[22]),dateiname)
habe ich geschaut wie viel in einer bestimmten Zelle ist. Hier ist es teilweise bei 108000 für eine einzelne Zelle.
Kann man nicht einfach prüfen und dann ggf. die Datein überspringen:

Code: Alles auswählen

for row in reader:         #schleife für jede zeile
            if summe(len(row[0])...len(row[63])) < 130000: #das in richtig
                row.append(dateiname)#schreibt in jede zeile hinten den vollen dateinamen dran
                compid, nummer, _, subname = dateiname.split("#") #"splittet" den namen mit der Trennung "#"
                row.extend([compid, nummer, subname])#setzt den geteilten Dateinamen in die letzten drei Spalten
                liste.append(row) #schreibt die zeilen in die liste
            else:
            gehe zu row+1 # und #das in richtig
            print(dateiname)
BlackJack hat geschrieben:Was sind denn das für Daten?
Das sind Informationen zu wissenschaftlichen Arbeiten. Das konkrete Problem (mit den großen Zellen) ist, dass es wissenschaftliche Publikationen (scheinbar im Medizinbereich) gibt wo ca. 400 Autoren angegeben sind. Und jeder hat eine Adresse und einen Namen einer Uni dabei. Da wird eine Zelle recht schnell voll. Ob der Inhalt einer Publikation mit 400 Autoren sinnvoll ist, steht wohl auch einem anderen Blatt ;)


VG kid

Re: Dateinamen in jede Zeile schreiben

Verfasst: Mittwoch 26. November 2014, 14:24
von BlackJack
@kidchino: Wenn die grösste Datei 1,5 MiB gross ist dann setz das Feldlimit einfach auf 2 MiB. Grösser als die grösste Datei kann ein Feld ja unmöglich sein. :-)

„field” ist ein Element einer CSV-Zeile. (Eine CSV-Zeile muss sich nicht auf eine Zeile in der Textdatei beschränken, denn man kann auch Zeilenumbrüche innerhalb von Zellen haben.)

Um ein ``len(row[index])`` machen zu können muss `row` ja gelesen worden sein, dafür darf kein Feld grösser als die eingestellte „field size” sein.

Wenn die Ausnahme aufgetreten ist, dann kann man die Folgedatensätze aus der Datei nicht mehr verarbeiten würde ich mal behaupten. Man kann dann also nur die Datei, oder den Rest davon ignorieren. Dafür behandelt man halt die Ausnahme entsprechend.

Re: Dateinamen in jede Zeile schreiben

Verfasst: Mittwoch 26. November 2014, 14:52
von jerch
kidchino hat geschrieben:Das sind Informationen zu wissenschaftlichen Arbeiten. Das konkrete Problem (mit den großen Zellen) ist, dass es wissenschaftliche Publikationen (scheinbar im Medizinbereich) gibt wo ca. 400 Autoren angegeben sind. Und jeder hat eine Adresse und einen Namen einer Uni dabei. Da wird eine Zelle recht schnell voll. Ob der Inhalt einer Publikation mit 400 Autoren sinnvoll ist, steht wohl auch einem anderen Blatt ;)
Naja pauschal kann man das nicht abwatschen. Im Gesundheitswesen gibt es Forschungsarbeiten, welche von großen Konsortien aus den Stakeholdern sektorübergreifend vorangetrieben werden. Und da haben vom klinischen Institut über Versicherer und Pharmafirmen bis zur IT-Abteilung alle ihr Bözelein beigetragen. Das können dann durchaus richtungsweisende Arbeiten sein, da diese sich oft mit strukturellen Fragen der Gesundheitsversorgung befassen.

Aber zurück zum Thema - wenn die großen Felder noch stark strukturierte Daten enthalten, lohnt evtl. eine weitere Normalisierung, z.B. bei der Autorenliste eine M2M-Relation.

Re: Dateinamen in jede Zeile schreiben

Verfasst: Mittwoch 26. November 2014, 18:37
von kidchino
@jerch
Jetzt hab ich einen Artikel mit 1139 Autoren über den Zelltod. Feiner Sache, aber nervt trotzdem ;)
BlackJack hat geschrieben:wenn die großen Felder noch stark strukturierte Daten enthalten, lohnt evtl. eine weitere Normalisierung, z.B. bei der Autorenliste eine M2M-Relation.
Das wäre super aber, die Inhalte so so unstrukturiert (innerhalb dieser Zelle), dass man es nicht sinnvoll trennen kann.

Ich probiere nun das Skript so umzubauen, dass es solange läuft bis das Limit erreicht ist und mir Dateiname und Zeile ausgibt und dann abbricht. So kann ich nach und nach die "zu langen" Zeilen per Hand ziehen und sie seperat behandeln. Genaugenommen passen diese Daten eh nicht inhaltlich zu dem was ich machen will ;)

Re: Dateinamen in jede Zeile schreiben

Verfasst: Mittwoch 26. November 2014, 21:13
von kidchino
So, ich bekomme es nicht hin :(
Weiter oben im Skript habe ich eine neue Grenze definiert:

Code: Alles auswählen

csv.field_size_limit(500000)
Daher kommt kein Fehler mehr und ich kann die Daten lesen und nutzen.

Jetzt würde ich gern jedes Element vergleichen, und mir ausgeben lassen an welcher Stelle das zu große Element ist.

Code: Alles auswählen

with open(os.path.join(pfad, dateiname), "rb") as f: #öffnet die jeweilige datei
        reader = csv.reader(f, delimiter="\t") #liest die zeilen aus, tabstop getrennt
        next(reader) #überspringt jeweils die erste zeile 
        
        for row in reader:   #schleife für jede zeile
            for i in range (0, 60,1):
                if len(row[i])> 131072:
                   print("STOP:" ,dateiname, "in Zeile: ROW","Element:", i)
                
Ich bekomme nun das Stop, den Dateinamen und das Element. Leider weiß ich nicht wie ich Zeilennummer ausgaben lassen könnte. Das müsste ja ein das row sein oder anhand der \t zu zählen sein. Ich bekomme es einfach nicht hin...

Jemand eine Idee?
LG
kid

Re: Dateinamen in jede Zeile schreiben

Verfasst: Mittwoch 26. November 2014, 21:18
von Sirius3
@kidchino: Das Feldgrößenlimit ist nur dazu da, kaputte Dateien zu erkennen, in denen z.B. ein schließendes Anführungszeichen fehlt. Wenn Du Dir sicher bist, dass das bei Deinen Dateien nicht vorkommt, brauchst Du das Feldgrößenlimit nicht zu beachten. Zählen kannst Du mit enumerate:

Code: Alles auswählen

for nr, row in enumerate(reader):
    ...

Re: Dateinamen in jede Zeile schreiben

Verfasst: Sonntag 30. November 2014, 18:03
von kidchino
@Sirius3:
Danke für den Tipp mit "enumerate". Ich hatte dann einfach eine Variable eingeführt bevor ich es von dir gelesen habe.
Und genau das habe ich damit auch gefunden, was Du gesagt hast.Es fehlte an drei Stellen ein "schließendes Anführungszeichen" und ich mit dem Skript finden:

Code: Alles auswählen

csv.field_size_limit(250000) #131072
[....]
readerzahl = 0
        for row in reader:   #schleife für jede zeile
            readerzahl += 1
            for i in range (0, 60, 1):
                if len(row[i]) > 100000:
                    print("Name:",dateiname,"Zeile:",readerzahl+1, "Element:",i+1,"Laenge:", len(row[i]))
 
Wenn ich ein wenig runtergehe, dann bspw. auf ">100000" dann finde auch auch die Felder wo zu (für mich) zu viel Inhalt ist.
Also klappt alles ganz gut :)
Danke