Python: разархивировать данные

Вопросы написания собственного программного кода (на любых языках)

Модератор: Olej

Аватара пользователя
Olej
Писатель
Сообщения: 21336
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Python: разархивировать данные

Непрочитанное сообщение Olej » 13 фев 2019, 13:17

Многие наборы данных в тестовых базах данных для отработки машинного обучения и моделей классификаторов (см. Модели классификаторов) - имеют архивный формат .Z, характерный для UNIX формат (Unix Compressed File).

Код: Выделить всё

olej@ACER:~/2019_WORK/own.WORK/MachineLearning$ python3
Python 3.7.2+ (default, Feb  2 2019, 14:31:48) 
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import urllib.request as url
>>> u = 'http://archive.ics.uci.edu/ml/machine-learning-databases/waveform/waveform.data.Z'
>>> response = url.urlopen( u )
>>> data = response.read()
>>> data[:30]
b'\x1f\x9d\x90-b\xb8\x901\x83E@\x175l\x18\x14x\xa3\x86A\x18\x03q\xb0\x80h\x03\x06\x0b\x19'

Код: Выделить всё

olej@ACER:~/2019_WORK/own.WORK/MachineLearning$ file waveform.data.Z 
waveform.data.Z: compress'd data 16 bits
Магическое число этого формата:

Код: Выделить всё

olej@ACER:~/2019_WORK/own.WORK/MachineLearning$ hexdump -bxn4 waveform.data.Z 
0000000 037 235 220 055                                                
0000000    9d1f    2d90                                                
0000004
FILE SIGNATURES TABLE
15 January 2019
...
1F 9D
TAR.Z Compressed tape archive file using standard (Lempel-Ziv-Welch) compression
Lempel–Ziv–Welch
Lempel–Ziv–Welch (LZW) is a universal lossless data compression algorithm created by Abraham Lempel, Jacob Ziv, and Terry Welch. It was published by Welch in 1984 as an improved implementation of the LZ78 algorithm published by Lempel and Ziv in 1978.
Разархивируется это добро самыми разными способами (утилитами):

Код: Выделить всё

olej@ACER:~/2019_WORK/own.WORK/AplitSoft/OrgDoc$ uncompress -k waveform.data.Z 

olej@ACER:~/2019_WORK/own.WORK/AplitSoft/OrgDoc$ gzip -dc waveform.data.Z > waveform.data

olej@ACER:~/2019_WORK/own.WORK/AplitSoft/OrgDoc$ zcat -dc waveform.data.Z > waveform.data

olej@ACER:~/2019_WORK/own.WORK/AplitSoft/OrgDoc$ cat waveform.data.Z | zcat -dc - > waveform.data

olej@ACER:~/2019_WORK/own.WORK/AplitSoft/OrgDoc$ ls -l waveform.data*
-rw-r--r-- 1 olej olej 555497 фев 12 13:53 waveform.data
-rw-r--r-- 1 olej olej 176908 фев  6 14:05 waveform.data.Z

Аватара пользователя
Olej
Писатель
Сообщения: 21336
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Python: разархивировать данные

Непрочитанное сообщение Olej » 13 фев 2019, 13:22

Olej писал(а): Разархивируется это добро самыми разными способами (утилитами):
Вопрос: как разархивировать этот сжатый формат данных в коде Python?
В Python есть несколько пакетов работы с архивированными данными (The Python Standard Library):
..
Data Compression and Archiving
zlib — Compression compatible with gzip
gzip — Support for gzip files
bz2 — Support for bzip2 compression
lzma — Compression using the LZMA algorithm
zipfile — Work with ZIP archives
tarfile — Read and write tar archive files
...
Но всё это работает с форматами из Windows, но не работает с .Z

Код: Выделить всё

>>> z = zipfile.ZipFile('waveform.data.Z', compression=zipfile.ZIP_LZMA )
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.7/zipfile.py", line 1222, in __init__
    self._RealGetContents()
  File "/usr/lib/python3.7/zipfile.py", line 1289, in _RealGetContents
    raise BadZipFile("File is not a zip file")
zipfile.BadZipFile: File is not a zip file

Код: Выделить всё

>>> import urllib.request as url
>>> response = url.urlopen( "http://archive.ics.uci.edu/ml/machine-learning-databases/waveform/waveform.data.Z" )
>>> import zlib
>>> data = response.read()
>>> type(data)
<class 'bytes'>
>>> data2=zlib.decompress( data ) 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
zlib.error: Error -3 while decompressing data: incorrect header check

Код: Выделить всё

>>> import gzip
>>> f = gzip.open('waveform.data.Z', 'rb')
>>> f.read()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.7/gzip.py", line 276, in read
    return self._buffer.read(size)
  File "/usr/lib/python3.7/gzip.py", line 463, in read
    if not self._read_gzip_header():
  File "/usr/lib/python3.7/gzip.py", line 411, in _read_gzip_header
    raise OSError('Not a gzipped file (%r)' % magic)
OSError: Not a gzipped file (b'\x1f\x9d')
Вопрос: как?

Аватара пользователя
Olej
Писатель
Сообщения: 21336
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Python: разархивировать данные

Непрочитанное сообщение Olej » 13 фев 2019, 13:42

Olej писал(а):Вопрос: как?
Использовать запуск внешней программы (zcat, gzip) из кода Python.
Проделываю это на небольшом тестовом файле:

Код: Выделить всё

olej@ACER:~/2019_WORK/own.WORK/MachineLearning/machine-learning-databases/waveform$ cat t1.txt 
123
456

olej@ACER:~/2019_WORK/own.WORK/MachineLearning/machine-learning-databases/waveform$ gzip -c t1.txt > t1.Z

olej@ACER:~/2019_WORK/own.WORK/MachineLearning/machine-learning-databases/waveform$ ls -l t1.*
-rw-r--r-- 1 olej olej  8 фев 12 15:15 t1.txt
-rw-r--r-- 1 olej olej 35 фев 12 15:17 t1.Z

olej@ACER:~/2019_WORK/own.WORK/MachineLearning/machine-learning-databases/waveform$ gzip -cd t1.Z
123
456
Теперь из кода Python:

Код: Выделить всё

>>> proc=subprocess.Popen( [ 'cat', 't1.Z' ], stdout=subprocess.PIPE, stderr=subprocess.PIPE )
>>> proc.pid
9862
>>> c = proc.communicate()
>>> t1 = c[0]
>>> t1
b'\x1f\x8b\x08\x08\xe7\xc6b\\\x00\x03t1.txt\x00342\xe6215\xe3\x02\x00\xb5e\xc7\xef\x08\x00\x00\x00'
...
...
>>> proc=subprocess.Popen( [ '/bin/sh', '-c', 'zcat', '-dc', '-' ], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE )
>>> proc.pid
9872
>>> proc.stdin.write( t1 )
35
>>> r = proc.communicate()
>>> r
(b'123\n456\n', b'')
>>> r[0].decode( "utf-8" )
'123\n456\n'
>>> print( r[0].decode( "utf-8" ) )
123
456
Отлично! Самое оно... (хотя хотелось бы из кода без использования внешних программ :cry: )

Аватара пользователя
Olej
Писатель
Сообщения: 21336
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Python: разархивировать данные

Непрочитанное сообщение Olej » 13 фев 2019, 13:53

Olej писал(а):Отлично! Самое оно... (хотя хотелось бы из кода без использования внешних программ :cry: )
Но только на реальных больших данных (считанные раньше в переменную data) этот номер не проходит!

Код: Выделить всё

>>> proc=subprocess.Popen( [ 'zcat', '-dc', '-' ], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE )
>>> proc.pid
9915
>>> proc.stdin.write( data )
^CTraceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyboardInterrupt
>>> r = proc.communicate()
>>> len(r[0])
65536
>>> print(r[0][-100:])
b',2.60,5.44,2.53,3.25,3.71,3.56,2.32,3.53,0.43,1.88,1.09,-0.45,1.00,1.58,0.11,0.84,1\n-0.35,0.57,0.91,'
Даже разархивированное начало данных совпадает с тем что надо .. Но! Характерный размер считанных данных 65535 подсказывает: проблема в размере буферов PIPE через которые происходит обмен!
Об этом же находим примечание в свежей онлайновой документации Python - subprocess — Subprocess management
Note The data read is buffered in memory, so do not use this method if the data size is large or unlimited.
И как можно обойти это ограничение я не нахожу ни в документации Python, ни в обсуждениях и вопросах в Интернет! :-(

Аватара пользователя
Olej
Писатель
Сообщения: 21336
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Python: разархивировать данные

Непрочитанное сообщение Olej » 13 фев 2019, 13:58

Olej писал(а):И как можно обойти это ограничение я не нахожу ни в документации Python, ни в обсуждениях и вопросах в Интернет! :-(
В конечном счёте, всё это можно сделать через промежуточные файлы, а не непосредственно в RAM ... но это грубовато будет ;-) :
- uncompress.py :

Код: Выделить всё

#!/usr/bin/python3 
# -*- coding: utf-8 -*- 
import sys, os, subprocess
import urllib.request as url

urld = 'http://archive.ics.uci.edu/ml/machine-learning-databases/waveform/waveform.data.Z'
if len( sys.argv ) > 1:
    urld = sys.argv[ 1 ]
print( urld )

try: 
    response = url.urlopen( urld )             # download from URL
except IOError as err: 
    print( 'wrong URL: {}'.format( err ) )
    sys.exit( 1 )
data = response.read()  

zname = urld.split( os.sep )[ -1 ] 
print( '{} - {} bytes length'.format( zname, len( data ) ) )
if 'Z' == zname.split( '.' )[ -1 ]:
    fw = os.open( zname, os.O_WRONLY | os.O_TRUNC | os.O_CREAT )
    os.write( fw, data )
    os.close( fw )
    proc = subprocess.Popen( [ 'gunzip', '-kfq', zname ] )
    dname = zname[ :zname.rfind( '.' ) ]
    print( '{} => {}'.format( zname, dname ) )   
    fr = os.open( dname, os.O_RDONLY )
    step = 100000
    data = ''
    while True:
        d = os.read( fr, step )
        data += d.decode( 'utf-8' ) #.splitlines()
        if len( d ) < step: break
    os.close( fr )
    print( '{} - {} bytes length'.format( dname, len( data ) ) )

data = data.splitlines()
print( len ( data ) )
print( data[ 0 ], '\n', data[ -1 ] )
print( len( data[ 0 ].split( ',' ) ) )

Код: Выделить всё

olej@ACER:~/2019_WORK/own.WORK/MachineLearning$ ./uncompress.py 
http://archive.ics.uci.edu/ml/machine-learning-databases/waveform/waveform.data.Z
waveform.data.Z - 176908 bytes length
waveform.data.Z => waveform.data
waveform.data - 555497 bytes length
5000
-1.23,-1.56,-1.75,-0.28,0.60,2.22,0.85,0.21,-0.20,0.89,1.08,4.20,2.89,7.75,4.59,3.15,5.12,3.32,1.20,0.24,-0.56,2 
 0.63,-0.07,2.71,2.55,3.36,3.22,3.69,4.67,3.45,3.87,4.15,1.46,-0.52,1.90,0.88,3.15,1.27,-0.53,0.09,0.01,0.60,1
22
Сравниваем:

Код: Выделить всё

olej@ACER:~/2019_WORK/own.WORK/MachineLearning$ head -n1 waveform.data
-1.23,-1.56,-1.75,-0.28,0.60,2.22,0.85,0.21,-0.20,0.89,1.08,4.20,2.89,7.75,4.59,3.15,5.12,3.32,1.20,0.24,-0.56,2
Это самое оно!

Аватара пользователя
Olej
Писатель
Сообщения: 21336
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: Python: разархивировать данные

Непрочитанное сообщение Olej » 13 фев 2019, 17:42

Olej писал(а):Многие наборы данных в тестовых базах данных для отработки машинного обучения и моделей классификаторов (см. Модели классификаторов) - имеют архивный формат .Z, характерный для UNIX формат (Unix Compressed File).
Эти наборы данных собираются начиная ещё с середины 90-х годов.
Поэтому ничего странного, что часть из них используют старый UNIX формат compress/uncompress.
Но их нужно тоже как-то разархивировать, причём разархивировать "в лёт", т.е. непосредственно загружая непосредственно с указанного URL.

Для проверки создаю архив .Z формата и разархивирую его обратно:

Код: Выделить всё

olej@ACER:~/2019_WORK/own.WORK/MachineLearning$ tar -c -Z -f ml.1.hist.Z ml.1.hist 

olej@ACER:~/2019_WORK/own.WORK/MachineLearning$ file ml.1.hist.Z 
ml.1.hist.Z: compress'd data 16 bits

olej@ACER:~/2019_WORK/own.WORK/MachineLearning$ hexdump -bx -n5 ml.1.hist.Z 
0000000 037 235 220 155 330                                            
0000000    9d1f    6d90    00d8                                        
0000005

olej@ACER:~/2019_WORK/own.WORK/MachineLearning$ tar -tvf ml.1.hist.Z 
-rw-r--r-- olej/olej     24148 2019-01-07 15:26 ml.1.hist

olej@ACER:~/2019_WORK/own.WORK/MachineLearning$ tar -x -Z -O -f ml.1.hist.Z > ml.1-2.hist

olej@ACER:~/2019_WORK/own.WORK/MachineLearning$ ls -l ml.1*
-rw-r--r-- 1 olej olej 24148 фев 13 16:23 ml.1-2.hist
-rw-r--r-- 1 olej olej 24148 янв  7 15:26 ml.1.hist
-rw-r--r-- 1 olej olej 11398 фев 13 16:13 ml.1.hist.Z

olej@ACER:~/2019_WORK/own.WORK/MachineLearning$ cmp ml.1.hist ml.1-2.hist

olej@ACER:~/2019_WORK/own.WORK/MachineLearning$ echo $?
0
Всё ОК ... даже магические числа файла совпадают.
Но ... то же самое, но относительно датасета :

Код: Выделить всё

olej@ACER:~/2019_WORK/own.WORK/MachineLearning$ tar -x -Z -O -f waveform.data.Z > waveform.data 
tar: Это не похоже на tar-архив
tar: Пропускается до следующего заголовка
tar: Завершение работы с состоянием неисправности из-за возникших ошибок
И, наконец, выясняется:

Код: Выделить всё

olej@ACER:~/2019_WORK/own.WORK/MachineLearning$ tar -x -Z -O -H oldgnu -f ml.1.hist.Z > ml.1-2.hist 

olej@ACER:~/2019_WORK/own.WORK/MachineLearning$ cmp ml.1.hist ml.1-2.hist

olej@ACER:~/2019_WORK/own.WORK/MachineLearning$ echo $?
0
Вот так, в формате oldgnu, всё оно работает...
Но пакет Python tarfile не знает старого формата: tarfile — Read and write tar archive files.

Ответить

Вернуться в «Программирование»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 3 гостя