Große Liste mit compress komprimieren ... ???

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.
CapJo
User
Beiträge: 26
Registriert: Donnerstag 27. April 2006, 13:17

7.15 zlib -- Compression compatible with gzip

For applications that require data compression, the functions in this module allow compression and decompression, using the zlib library. The zlib library has its own home page at http://www.gzip.org/zlib/. There are known incompatibilities between the Python module and versions of the zlib library earlier than 1.1.3; 1.1.3 has a security vulnerability, so we recommend using 1.1.4 or later.

compress( string[, level])

Compresses the data in string, returning a string contained compressed data. level is an integer from 1 to 9 controlling the level of compression; 1 is fastest and produces the least compression, 9 is slowest and produces the most. The default value is 6. Raises the error exception if any error occurs.

Es geht um eine Liste mit 2,5 Millionen Einträgen (22 MB) und die würde ich gerne mit compress komprimieren.

Leider verlangt compress einen String ... wie kann ich das Ganze casten? Und dann nach dem dekomprimieren wieder zurückcasten?

Ich könnts erst in einen String umwandeln ... und dann wieder mit eval einlesen, aber das dauert ewig. Da lohnt sich die Kompression dann gar nicht.

Gibts da ne Lösung um das ganze zur Laufzeit zu komprimieren?
[/quote]
Benutzeravatar
keppla
User
Beiträge: 483
Registriert: Montag 31. Oktober 2005, 00:12

Vielleicht mit Pickle

http://www.python.org/doc/1.5.2p2/lib/m ... ickle.html

Die "Dateien" die man übergeben muss, müssen lediglich objekte mit write(string) bzw read/readline sein, da kannst du dir einen Adapter zwischenbauen.
pr0stAta
User
Beiträge: 271
Registriert: Freitag 17. September 2004, 11:49
Wohnort: Bremen

Leider verlangt compress einen String ... wie kann ich das Ganze casten? Und dann nach dem dekomprimieren wieder zurückcasten?
Casten ist hier wohl der falsche Ausdruck, aber egal :>
Ich weiss natürlich nicht wie deine Liste nun aussieht und wie das Kompremieren funktioniert, aber du könntest aus einer Liste einen langen
String mit einem bestimmten Trennzeichen machen, so könntest du nach dem dekomprimieren den string splitten
Beispiel wenn deine Liste die Zahlen 1-1000 hätte:

Code: Alles auswählen

test = ",".join([str(item) for item in range(1000)])
Nun hättest du einen langen String, 1,2,3,4,5,6....
Danach wieder aufsplitten:

Code: Alles auswählen

test2 = test.split(",")
Vielleicht hilft dir das ja weiter.
Gruss
CapJo
User
Beiträge: 26
Registriert: Donnerstag 27. April 2006, 13:17

Danke für den Tipp pr0stAta, das Problem ist jedoch, dass da Integerwerte in der Liste stehen und ich müsste ja dann wieder auf jeden Wert in der Liste ein eval anwenden, da es ja nach dem splitten jetzt Strings sind.

In C oder in Java würd ich das ganze einfach in einen anderen Typ casten ... besteht ja sowieso alles aus 0en und 1en. Danach würd ichs komprimieren und dann wieder demkomprimieren und in den ursprünglichen Typ casten.

So würd das ganze am effizientesten gehen.
Benutzeravatar
keppla
User
Beiträge: 483
Registriert: Montag 31. Oktober 2005, 00:12

In C oder in Java würd ich das ganze einfach in einen anderen Typ casten ... besteht ja sowieso alles aus 0en und 1en. Danach würd ichs komprimieren und dann wieder demkomprimieren und in den ursprünglichen Typ casten.
Dann zeig mir mal, wie du in Java ein int zu einem String castest ;)

Ne, mal ernsthaft: Pickle ist Pythons Ansatz, Variablen zu serialisieren, und dürfte auch dem entsprechend schnell sein.
Wenn du erstmal anfängst, dein Ding zu Parsen (denn nichts anders ist die split-Lösung) wird das mit ziemlicher sicherheit dem eigentlichen Ziel, nämlich weniger Speicherverbrauch entgegenwirken.

Aber mal davon ab, warum möchtest du die Liste Packen? 22MB sind ja eigentlich nicht soo die katasrophe.
Benutzeravatar
DatenMetzgerX
User
Beiträge: 398
Registriert: Freitag 28. April 2006, 06:28
Wohnort: Zürich Seebach (CH)

die Liste packen??? für was. Was braucht mehr speicher. Die liste so zu belassen, oder die Liste zu komprimieren und bei jedem zugriff wieder dekomprimieren ;)

oder was falsch verstanden :roll:
CapJo
User
Beiträge: 26
Registriert: Donnerstag 27. April 2006, 13:17

Die 22 MB sind jetzt der derzeitige Fall, es können aber auch schnell mal größere Datenaufkommen werden.

Es werden auf 32 Datenkanälen im microsekundenbereich Daten aufgezeichnet. Diese 22 MB sind in gerade mal in 1,25 Sekunden zusammengekommen.

Die Liste sollte gepackt werden, um Speicherplatz und Zeit, beim Ablegen der Daten, auf der Festplatte, zu sparen.
Benutzeravatar
keppla
User
Beiträge: 483
Registriert: Montag 31. Oktober 2005, 00:12

das dürfte "premature optimisation", und somit "the root of all evil" sein.

wenn das Problem ist, dass sich der Speicher zu schnell füllt, wirst du es nicht dadurch beheben, dass du den Speicherinhalt Packst, da weiterhin mehr reinkommen als rausgehen wird.

einen Geschwindigkeitsvorteil beim Schreiben auf die Platte wird sich auch nicht einstellen, da (aufgrund des Virtuellen Speichers) sich der krams evtl sowieso schon auf der Platte befindet.
Da dürfte es einfacher sein, aus sicht des Programmes die Daten einfach in eine Datei zu schreiben, die dann in einem gepackten Dateisystem liegt.

Kurz gefasst: Speichermanagement ist nicht Aufgabe deines Programms.
Joghurt
User
Beiträge: 877
Registriert: Dienstag 15. Februar 2005, 15:07

Ich stimme mit keppla überein, dass es in deinem Falle schlecht ist, den String zu komprimieren.

Nur der Vollständigkeit halber hier eine Möglichkeit

Code: Alles auswählen

import zlib
import cPickle
l = range(100)
c = zlib.compress(cPickle.dumps(l))
del l
l = cPickle.loads(zlib.decompress(c))
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

CapJo hat geschrieben:besteht ja sowieso alles aus 0en und 1en.
Wie jetzt? Die Liste hat wirklich nur 0 und 1 ??? IMHO verbraucht dann ein String weniger Speicher, behaupte ich einfach mal so... Also warum dann nicht die "Daten" als String speichern?

Btw. was sind das für Daten, wenn es nur 0 und 1 ist???

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
CapJo
User
Beiträge: 26
Registriert: Donnerstag 27. April 2006, 13:17

Sorry, dass ich jetzt erst wieder Antworte ...

Das Modul, das wir benutzen, wurde in Indien implementiert und ist
in der derzeitigen Fassung nicht zu gebrauchen (viel zu lahm und viel zu großer Speicherverbrauch)... deshalb versuchen
wir da etwas zu optimieren

Also das ganze läuft wie folgt ab:

Die Daten kommen über einen C-Buffer als Array von Integerwerten (viele Millionen Werte) herein.

In diesem speziellen Fall sind das Spannungswerte, die im späteren Programablauf als Binärwerte interpretiert werden.

Diesen Buffer konnte man per cpickle nicht serialisieren und deshalb wurde aus diesem Array eine Liste gemacht und die kann man dann per dump ablegen.

Leider sind die Datenaufkommen sehr groß und deshalb sollte eine Kompression her, deshalb war die Idee Nahe Kompress zu benutzen.

Ein weiteres Problem ist mit cpickle aufgetreten, der will die Daten nicht in Binärform abspeichern obwohl ich das so angegeben habe.

... es wird hier aus Kompatibilitätsgründen die Python Version 2.2 benutzt.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Macht es da nicht evtl. Sinn eine echte DB für her zu nehmen???

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
keppla
User
Beiträge: 483
Registriert: Montag 31. Oktober 2005, 00:12

Leider sind die Datenaufkommen sehr groß und deshalb sollte eine Kompression her, deshalb war die Idee Nahe Kompress zu benutzen.
Um welches Problem zu lösen? Denn Probleme könnte ich mir mehrere denken.

Wenn das Problem ist, dass du alle Daten vorrätig halten musst, ist Compress die falsche Lösung, imho, da du dann Speicher und Rechenzeit gegen Speicher tauscht (kompression/dekompression brauch ebenfalls Speicher).

Eigentlich gibt es dann nur zwei Fälle:
1. die Daten im Speicher sind "etwas" zu gross, dann ignorieren und auf virtuellen Speicher vertrauen, oder stückeln und auf die Platte schreiben, und bei Bedarf das nötige Stückchen laden.
2. die Daten sind "deutlich" zu gross, das schreit absolut nach Datenbank, denn da hilft dir dann auch Packen nicht, da du dabei ja nicht damit rechnen kannst, dass es dir immer 90% oder so wegkomprimiert.

Eigentlich lässt sich das vorgehen imho auf die Frage reduzieren: warum möchtest du die Daten überhaupt im Speicher haben? was genau willst du damit machen?
CapJo
User
Beiträge: 26
Registriert: Donnerstag 27. April 2006, 13:17

Danke für die Denkanstöße.

Die Daten müssen nicht dauerhaft im Speicher gehalten werden und die Daten sollten schnell ausgetauscht werden können. Die Datenmenge wird als Rohdaten aufgezeichnet (Integerwerte).

Diese Rohdaten sollen gespeichert werden um später dann ausgewertet zu werden.

Dabei wird die Datenmenge durchlaufen und eine grafische Ausgaben mit matplotlib gezeichnet.

Wir haben einen Lösungsansatz gefunden, der vielleicht nicht der elleganteste ist aber er war schnell implementiert.

Die Daten lagen als ctype-Array vor und werden in eine Liste gewandelt.

Diese wird per dumps aus cPickle in einen String umgewandelt und dieser String wird per compress aus dem zlib Modul komprimiert.

Anschließend wird per dump aus cPickle die Daten binär in eine Datei geschrieben.

Funktioniert wirklich gut und schnell.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Wenn es nur INT Werte sind, wäre es IMHO schneller/kleiner ihr würtet statt cpickel einfach "-".join(...) / .split("-") nehmen ;) Pickel hat auch einen gewissen "Overhead"...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
CapJo
User
Beiträge: 26
Registriert: Donnerstag 27. April 2006, 13:17

Nach dem splitten hab ich ja eine Liste mit vielen Strings. Ich will jedoch den Wert des String und dann wär wohl ein int() nötig.

Code: Alles auswählen

import time, cPickle, zlib
#-------------------------------------------------------------------------------
integers = range(4000000)

#Einmal durchlaufen für "gleiche" Bedingungen
for i in integers:
    pass
#-------------------------------------------------------------------------------
start = time.time()

string_join = ",".join([str(item) for item in integers]) 
  
integer_list = string_join.split(",")

##for string in string_join.split(","):
##    integer_list.append(int(string))
    
map(int, integer_list)
    
print integer_list[-1]

end = time.time()

print "Time of join / split: " + str(end - start)
print "Size of String resulting String join / split: " + str(len(string_join))
print "Compressed Size: " + str(len(zlib.compress(string_join))) 

#-------------------------------------------------------------------------------

start = time.time()

string_pickle = cPickle.dumps(integers)
integer_list_pickle = cPickle.loads(string_pickle)

print integer_list_pickle[-1]

end = time.time()

print "Time of cPickle: " + str(end - start)
print "Size of String resulting String cPickle: " + str(len(string_pickle))
print "Compressed Size: " + str(len(zlib.compress(string_pickle))) 
3999999
Time of join / split: 17.2139999866
Size of String resulting String join / split: 30888889
Compressed Size: 8443383
3999999
Time of cPickle: 9.51300001144
Size of String resulting String cPickle: 38888896
Compressed Size: 8880297

cPickle scheint schneller zu sein, als join + split + int.
Zuletzt geändert von CapJo am Montag 19. Juni 2006, 15:24, insgesamt 1-mal geändert.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Hast recht. Hätte ich nicht gedacht!
Naja wobei cPickle ja auch in C geschrieben ist. Gegenüber dem normalen Pickle ist die Join-Split Variante aber besser...

Im übrigen kannst du es mit cPickle.HIGHEST_PROTOCOL noch eine ganze Ecke kleiner haben! Außerdem gehts dann noch ein wenig schneller...
Siehe: http://docs.python.org/lib/node64.html

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: utf-8 -*-

import time, cPickle, pickle

integers = range(1000000)



print "Time of join / split"
start = time.time()

string_join = ",".join([str(item) for item in integers])
integer_list = [int(i) for i in string_join.split(",")]

print "%.2fSec" % (time.time() - start)
print "Size:", len(string_join)
print "-"*79



print "cPickle normal"
start = time.time()

string_pickle = cPickle.dumps(integers)
integer_list_pickle = cPickle.loads(string_pickle)

print "%.2fSec" % (time.time() - start)
print "Size:", len(string_pickle)
print "-"*79



print "cPickle HIGHEST_PROTOCOL:"
start = time.time()

string_pickle = cPickle.dumps(integers, cPickle.HIGHEST_PROTOCOL)
integer_list_pickle = cPickle.loads(string_pickle)

print "%.2fSec" % (time.time() - start)
print "Size:", len(string_pickle)
print "-"*79



print "normal pickle:"
start = time.time()

string_pickle = pickle.dumps(integers, pickle.HIGHEST_PROTOCOL)
integer_list_pickle = pickle.loads(string_pickle)

print "%.2fSec" % (time.time() - start)
print "Size:", len(string_pickle)
Ausgabe:
Time of join / split
2.92Sec
Size: 6888889
-------------------------------------------------------------------------------
cPickle normal
1.56Sec
Size: 8888896
-------------------------------------------------------------------------------
cPickle HIGHEST_PROTOCOL:
0.36Sec
Size: 4870678
-------------------------------------------------------------------------------
normal pickle:
8.42Sec
Size: 4870678

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
CapJo
User
Beiträge: 26
Registriert: Donnerstag 27. April 2006, 13:17

Wo wir gerade bei Performance sind:

Ich hab getestet wie groß der Unterschied zwischen einer For-Schleife und der map-Funktion ist.

Bei vielen Objekten ist es lohnenswert die map-Funktion zu benutzen. Sie ist aber nicht so flexibel und sicher nicht überall einsetzbar.

Code: Alles auswählen

import time

integers = range(10000000)
#-------------------------------------------------------------------------------
start = time.time()
map(abs, integers)
end = time.time()

print "Time of map: " + str(end - start)
#-------------------------------------------------------------------------------
start = time.time()
list = [abs(x) for x in integers]
end = time.time()

print "Time of for-loop with new list: " + str(end - start)
#-------------------------------------------------------------------------------
start = time.time()
for x in integers:
    abs(x)
    
end = time.time()

print "Time of for-loop without new list: " + str(end - start)
Time of map: 1.64199995995
Time of for-loop with new list: 6.83899998665
Time of for-loop without new list: 2.99499988556
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Hm, komisch der Unterschied zwischen

Code: Alles auswählen

integer_list = [int(i) for i in string_join.split(",")]
und

Code: Alles auswählen

map(int, string_join.split(","))
ist bei mir nicht so groß.

Aber immerhin! Ich dachte eigentlich das LCs schnell sind oder zumindest gleich schnell wie eine normale for-Schleife...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
CapJo
User
Beiträge: 26
Registriert: Donnerstag 27. April 2006, 13:17

Der Geschwindigkeitsvorteil dürfte wieder mal darin liegen, dass die map()-Funktion in C implementiert ist.

Der Geschwindigkeitsvorteil fällt jedoch umso geringer aus desto länger die Funktion dauert ist die man wiederholt aufruft.

Ich hab es mit der abs()-Funktion getestet, da diese relativ schnell sein sollte.
Antworten