pandas .read_sas () для чтения файла SAS .xpt не работает с файлом, хранящимся в Google Cloud Storage (GCS) - PullRequest
1 голос
/ 25 марта 2020

Я пытаюсь прочитать файл .XPT в Pandas DataFrame. Это работает, если файл является локальным, но не если файл хранится в GCS.

Я загрузил пример данных в GCS, используя:

!curl -L https://wwwn.cdc.gov/Nchs/Nhanes/2017-2018/DEMO_J.XPT | gsutil cp - gs://my-bucket/sas_sample/Nchs/Nhanes/2017-2018/DEMO_J.XPT

Я также загрузил файл локально, используя:

mkdir data
!curl https://wwwn.cdc.gov/Nchs/Nhanes/2017-2018/DEMO_J.XPT -o data/DEMO_J.XPT

Я попробовал следующее с GCS, но ни одна из них не работает:

import pandas as pd
import gcsfs

fs = gcsfs.GCSFileSystem(project='my-project')
with fs.open('my-bucket/sas_sample/Nchs/Nhanes/2017-2018/DEMO_J.XPT') as f:
    df = pd.read_sas(f,format='xport')

import pandas as pd

filepath = 'gs://my-bucket/sas_sample/Nchs/Nhanes/2017-2018/DEMO_J.XPT'
df = pd.read_sas(filepath, format='xport', encoding='utf-8')
df.head(10)

Они оба возвращают следующую ошибку:

/opt/conda/anaconda/lib/python3.7/site-packages/pandas/io/sas/sas_xport.py in __init__(self, filepath_or_buffer, index, encoding, chunksize)
    278             contents = filepath_or_buffer.read()
    279             try:
--> 280                 contents = contents.encode(self._encoding)
    281             except UnicodeEncodeError:
    282                 pass

AttributeError: 'bytes' object has no attribute 'encode'

Также сейчас пробовал с TensorFlow, который не работает:

from tensorflow.python.lib.io import file_io
import pandas as pd

filepath = 'gs://my-bucket/sas_sample/Nchs/Nhanes/2017-2018/DEMO_J.XPT'

with file_io.FileIO(filepath, 'r') as f:

    # ISO-8859-1
    # utf-8
    # utf-16
    # latin-1
    df = pd.read_sas(f, format='xport', encoding='utf-8')

df.head(5)

Возвращает ошибку:

---------------------------------------------------------------------------
UnicodeDecodeError                        Traceback (most recent call last)
<ipython-input-60-fb02f0706587> in <module>
     10     # utf-16
     11     # latin-1
---> 12     df = pd.read_sas(f, format='xport', encoding='utf-8')
     13 
     14 df.head(5)

/opt/conda/anaconda/lib/python3.7/site-packages/pandas/io/sas/sasreader.py in read_sas(filepath_or_buffer, format, index, encoding, chunksize, iterator)
     68 
     69         reader = XportReader(
---> 70             filepath_or_buffer, index=index, encoding=encoding, chunksize=chunksize
     71         )
     72     elif format.lower() == "sas7bdat":

/opt/conda/anaconda/lib/python3.7/site-packages/pandas/io/sas/sas_xport.py in __init__(self, filepath_or_buffer, index, encoding, chunksize)
    276         else:
    277             # Copy to BytesIO, and ensure no encoding
--> 278             contents = filepath_or_buffer.read()
    279             try:
    280                 contents = contents.encode(self._encoding)

/opt/conda/anaconda/lib/python3.7/site-packages/tensorflow_core/python/lib/io/file_io.py in read(self, n)
    126       length = n
    127     return self._prepare_value(
--> 128         pywrap_tensorflow.ReadFromStream(self._read_buf, length))
    129 
    130   @deprecation.deprecated_args(

/opt/conda/anaconda/lib/python3.7/site-packages/tensorflow_core/python/lib/io/file_io.py in _prepare_value(self, val)
     96       return compat.as_bytes(val)
     97     else:
---> 98       return compat.as_str_any(val)
     99 
    100   def size(self):

/opt/conda/anaconda/lib/python3.7/site-packages/tensorflow_core/python/util/compat.py in as_str_any(value)
    137   """
    138   if isinstance(value, bytes):
--> 139     return as_str(value)
    140   else:
    141     return str(value)

/opt/conda/anaconda/lib/python3.7/site-packages/tensorflow_core/python/util/compat.py in as_text(bytes_or_text, encoding)
    107     return bytes_or_text
    108   elif isinstance(bytes_or_text, bytes):
--> 109     return bytes_or_text.decode(encoding)
    110   else:
    111     raise TypeError('Expected binary or unicode string, got %r' % bytes_or_text)

UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 2967: invalid start byte

Но следующее работает правильно, когда файл локальный:

import pandas as pd

filepath = 'data/DEMO_J.XPT'
df = pd.read_sas(filepath, format='xport', encoding='utf-8')
df.head(10)

Ответы [ 2 ]

1 голос
/ 27 марта 2020

Это ошибка 33069 pandas. Соединитель ввода-вывода SAS неправильно полагает, что все файловые буферы открыты в текстовом режиме.

Я исправил свой локальный site-packages/pandas/io/sas/sas_xport.py со следующим изменением и смог прочитать кадр данных:

class XportReader(BaseIterator):
    __doc__ = _xport_reader_doc

    def __init__(
        self, filepath_or_buffer, index=None, encoding="ISO-8859-1", chunksize=None
    ):

        self._encoding = encoding
        self._lines_read = 0
        self._index = index
        self._chunksize = chunksize

        if isinstance(filepath_or_buffer, str):
            (
                filepath_or_buffer,
                encoding,
                compression,
                should_close,
            ) = get_filepath_or_buffer(filepath_or_buffer, encoding=encoding)

        if isinstance(filepath_or_buffer, (str, bytes)):
            self.filepath_or_buffer = open(filepath_or_buffer, "rb")
        else:
            # Copy to BytesIO, and ensure no encoding
            contents = filepath_or_buffer.read()
            try:
                # NEW LINE HERE: Don't convert to binary if it's already bytes.
                if hasattr(contents, "encode"):
                    contents = contents.encode(self._encoding)
            except UnicodeEncodeError:
                pass
            self.filepath_or_buffer = BytesIO(contents)

        self._read_header()

PR 33070 ожидает , что решает эту проблему. Когда объединено и pandas 1.1.0 выпущено, ручной патч больше не будет нужен.

1 голос
/ 26 марта 2020

Похоже, код на самом деле не обновляется для Python 3. Вы можете попытаться исправить библиотеку, удалив код .encode ('utf-8'), поскольку он вам не нужен в Python 3. См. : https://docs.python.org/3/library/stdtypes.html#binary -sequence-types-bytes-bytearray-memoryview

Альтернативно, вы можете использовать тензор потока вместо предохранителя gcs:

from tensorflow.python.lib.io import file_io
with file_io.FileIO('gs://my-bucket/.../DEMO_J.XPT', 'r') as f:
  df = pd.read_sas(f, format='xport')
...