Spec-Zone.ru › NumPy 1.19

Импорт данных с помощью genfromtxt

NumPy предоставляет несколько функций для создания массивов из табличных данных. Здесь мы сосредоточимся на функции genfromtxt.

Вкратце, genfromtxt выполняет два основных цикла. Первый цикл преобразует каждую строку файла в последовательность строк. Второй цикл преобразует каждую строку в соответствующий тип данных. Этот механизм медленнее, чем одноцикловый, но предоставляет большую гибкость. В частности, genfromtxt может учитывать пропущенные данные, в то время как другие более быстрые и простые функции, такие как loadtxt, этого не могут.

Примечание

При приведении примеров мы будем использовать следующие соглашения:

>>> import numpy as np
>>> from io import StringIO

Определение входных данных

Единственным обязательным аргументом функции genfromtxt является источник данных. Это может быть строка, список строк, генератор или открытый файл-подобный объект с методом read, например, файл или io.StringIO объект. Если предоставлена ​​одна строка, она предполагается именем локального или удаленного файла. Если предоставлен список строк или генератор, возвращающий строки, каждая строка обрабатывается как одна строка файла. При передаче URL-адреса удаленного файла файл автоматически загружается в текущую директорию и открывается.

Распознаваемые типы файлов — текстовые файлы и архивы. В настоящее время функция распознает gzip и bz2 (bzip2) архивы. Тип архива определяется из расширения файла: если имя файла заканчивается на '.gz', ожидается gzip архив; если оно заканчивается на 'bz2', предполагается bzip2 архив.

Разделение строк на столбцы

Аргумент delimiter

После определения и открытия файла для чтения, genfromtxt разбивает каждую непустую строку на последовательность строк. Пустые или комментарии строки просто пропускаются. Ключевое слово delimiter используется для определения способа разделения.

Часто один символ обозначает разделитель между столбцами. Например, файлы с разделителем запятыми (CSV) используют запятую (,) или точку с запятой (;) в качестве разделителя:

>>> data = u"1, 2, 3\n4, 5, 6"
>>> np.genfromtxt(StringIO(data), delimiter=",")
array([[ 1.,  2.,  3.],
       [ 4.,  5.,  6.]])

Еще одним распространенным разделителем является "\t", символ табуляции. Однако мы не ограничиваемся одним символом, любая строка подойдет. По умолчанию genfromtxt предполагает delimiter=None, что означает, что строка разделяется по пробелам (включая табуляции), а последовательные пробелы считаются одним пробелом.

В качестве альтернативы, мы можем иметь дело с файлом с фиксированной шириной, где столбцы определены заданным количеством символов. В этом случае необходимо установить delimiter на одно целое число (если все столбцы имеют одинаковый размер) или на последовательность целых чисел (если столбцы могут иметь разные размеры):

>>> data = u"  1  2  3\n  4  5 67\n890123  4"
>>> np.genfromtxt(StringIO(data), delimiter=3)
array([[   1.,    2.,    3.],
       [   4.,    5.,   67.],
       [ 890.,  123.,    4.]])
>>> data = u"123456789\n   4  7 9\n   4567 9"
>>> np.genfromtxt(StringIO(data), delimiter=(4, 3, 2))
array([[ 1234.,   567.,    89.],
       [    4.,     7.,     9.],
       [    4.,   567.,     9.]])

Аргумент autostrip

По умолчанию, когда строка разбивается на серию строк, отдельные элементы не очищаются от начальных и конечных пробелов. Это поведение можно переопределить, установив необязательный аргумент autostrip в значение True:

>>> data = u"1, abc , 2\n 3, xxx, 4"
>>> # Without autostrip
>>> np.genfromtxt(StringIO(data), delimiter=",", dtype="|U5")
array([['1', ' abc ', ' 2'],
       ['3', ' xxx', ' 4']], dtype='<U5')
>>> # With autostrip
>>> np.genfromtxt(StringIO(data), delimiter=",", dtype="|U5", autostrip=True)
array([['1', 'abc', '2'],
       ['3', 'xxx', '4']], dtype='<U5')

Аргумент comments

Необязательный аргумент comments используется для определения строки символов, которая отмечает начало комментария. По умолчанию genfromtxt предполагает comments='#'. Маркер комментария может находиться в любом месте строки. Любой символ, присутствующий после маркера комментария(ей), просто игнорируется:

>>> data = u"""#
... # Skip me !
... # Skip me too !
... 1, 2
... 3, 4
... 5, 6 #This is the third line of the data
... 7, 8
... # And here comes the last line
... 9, 0
... """
>>> np.genfromtxt(StringIO(data), comments="#", delimiter=",")
array([[1., 2.],
       [3., 4.],
       [5., 6.],
       [7., 8.],
       [9., 0.]])

Добавлено в версии 1.7.0: Если comments установлено в значение None, никакие строки не обрабатываются как комментарии.

Примечание

Существует одно важное исключение из этого поведения: если необязательный аргумент names=True, первая строка с комментарием будет проверена на имена.

Пропускание строк и выбор столбцов

Аргументы skip_header и skip_footer

Наличие заголовка в файле может помешать обработке данных. В этом случае необходимо использовать необязательный аргумент skip_header. Значение этого аргумента должно быть целым числом, которое соответствует количеству строк, которые нужно пропустить в начале файла, прежде чем выполнить какие-либо другие действия. Аналогично, мы можем пропустить последние n строки файла, используя атрибут skip_footer и присвоив ему значение n:

>>> data = u"\n".join(str(i) for i in range(10))
>>> np.genfromtxt(StringIO(data),)
array([ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.])
>>> np.genfromtxt(StringIO(data),
...               skip_header=3, skip_footer=5)
array([ 3.,  4.])

По умолчанию skip_header=0 и skip_footer=0, что означает, что никакие строки не пропускаются.

Аргумент usecols

В некоторых случаях нас интересуют не все столбцы данных, а только несколько из них. Мы можем выбрать, какие столбцы импортировать, с помощью аргумента usecols. Этот аргумент принимает одно целое число или последовательность целых чисел, соответствующих индексам столбцов для импорта. Помните, что по соглашению первый столбец имеет индекс 0. Отрицательные целые числа ведут себя так же, как и обычные отрицательные индексы Python.

Например, если мы хотим импортировать только первый и последний столбцы, мы можем использовать usecols=(0, -1):

>>> data = u"1 2 3\n4 5 6"
>>> np.genfromtxt(StringIO(data), usecols=(0, -1))
array([[ 1.,  3.],
       [ 4.,  6.]])

Если у столбцов есть имена, мы также можем выбрать, какие столбцы импортировать, указав их имя в аргументе usecols, либо как последовательность строк, либо как строку с разделителем запятыми:

>>> data = u"1 2 3\n4 5 6"
>>> np.genfromtxt(StringIO(data),
...               names="a, b, c", usecols=("a", "c"))
array([(1.0, 3.0), (4.0, 6.0)],
      dtype=[('a', '<f8'), ('c', '<f8')])
>>> np.genfromtxt(StringIO(data),
...               names="a, b, c", usecols=("a, c"))
    array([(1.0, 3.0), (4.0, 6.0)],
          dtype=[('a', '<f8'), ('c', '<f8')])

Выбор типа данных

Основной способ управления тем, как последовательности строк, которые мы считали из файла, преобразуются в другие типы, — это установка аргумента dtype. Допустимые значения для этого аргумента:

  • один тип, например, dtype=float. Результат будет 2D с заданным типом данных, если не было присвоено имя каждому столбцу с помощью аргумента names (см. ниже). Обратите внимание, что dtype=float является значением по умолчанию для genfromtxt.
  • последовательность типов, например, dtype=(int, float, float).
  • строка с разделителем запятыми, например, dtype="i4,f8,|U3".
  • словарь с двумя ключами 'names' и 'formats'.
  • последовательность кортежей (name, type), например, dtype=[('A', int), ('B', float)].
  • существующий объект numpy.dtype.
  • специальное значение None. В этом случае тип столбцов будет определяться из самих данных (см. ниже).

Во всех случаях, кроме первого, результатом будет одномерный массив со структурированным типом данных. Этот тип данных имеет столько полей, сколько элементов в последовательности. Имена полей определяются ключевым словом names.

Когда dtype=None, тип каждого столбца определяется итеративно из его данных. Мы начинаем с проверки, можно ли преобразовать строку в булево значение (то есть, если строка совпадает с true или false в нижнем регистре); затем, можно ли преобразовать ее в целое число, затем в число с плавающей запятой, затем в комплексное и, наконец, в строку. Это поведение можно изменить, изменив маппер по умолчанию класса StringConverter.

Опция dtype=None предоставляется для удобства. Однако она значительно медленнее, чем явное задание типа данных.

Установка имен

Аргумент names

Естественный подход при работе с табличными данными — назначение имени каждому столбцу. Первая возможность — использовать явный структурированный тип данных, как упоминалось ранее:

>>> data = StringIO("1 2 3\n 4 5 6")
>>> np.genfromtxt(data, dtype=[(_, int) for _ in "abc"])
array([(1, 2, 3), (4, 5, 6)],
      dtype=[('a', '<i8'), ('b', '<i8'), ('c', '<i8')])

Другая более простая возможность — использовать ключевое слово names с последовательностью строк или строкой с разделителем запятыми:

>>> data = StringIO("1 2 3\n 4 5 6")
>>> np.genfromtxt(data, names="A, B, C")
array([(1.0, 2.0, 3.0), (4.0, 5.0, 6.0)],
      dtype=[('A', '<f8'), ('B', '<f8'), ('C', '<f8')])

В примере выше мы использовали тот факт, что по умолчанию dtype=float. При указании последовательности имен мы принудительно устанавливаем результат в структурированный тип данных.

Иногда нам может потребоваться определить имена столбцов из самих данных. В этом случае мы должны использовать ключевое слово names со значением True. Тогда имена будут считываться из первой строки (после skip_header), даже если строка прокомментирована:

>>> data = StringIO("So it goes\n#a b c\n1 2 3\n 4 5 6")
>>> np.genfromtxt(data, skip_header=1, names=True)
array([(1.0, 2.0, 3.0), (4.0, 5.0, 6.0)],
      dtype=[('a', '<f8'), ('b', '<f8'), ('c', '<f8')])

Значение по умолчанию для names равно None. Если мы зададим любое другое значение для ключевого слова, новые имена заменят имена полей, которые мы могли определить с помощью типа данных:

>>> data = StringIO("1 2 3\n 4 5 6")
>>> ndtype=[('a',int), ('b', float), ('c', int)]
>>> names = ["A", "B", "C"]
>>> np.genfromtxt(data, names=names, dtype=ndtype)
array([(1, 2.0, 3), (4, 5.0, 6)],
      dtype=[('A', '<i8'), ('B', '<f8'), ('C', '<i8')])

Аргумент defaultfmt

Если names=None но ожидается структурированный тип данных, имена определяются с использованием стандартного значения по умолчанию NumPy "f%i", давая такие имена, как f0, f1 и так далее:

>>> data = StringIO("1 2 3\n 4 5 6")
>>> np.genfromtxt(data, dtype=(int, float, int))
array([(1, 2.0, 3), (4, 5.0, 6)],
      dtype=[('f0', '<i8'), ('f1', '<f8'), ('f2', '<i8')])

Точно так же, если мы не предоставим достаточно имен, чтобы соответствовать длине типа данных, недостающие имена будут определены с помощью этой шаблона по умолчанию:

>>> data = StringIO("1 2 3\n 4 5 6")
>>> np.genfromtxt(data, dtype=(int, float, int), names="a")
array([(1, 2.0, 3), (4, 5.0, 6)],
      dtype=[('a', '<i8'), ('f0', '<f8'), ('f1', '<i8')])

Мы можем переопределить это значение по умолчанию с помощью аргумента defaultfmt , который принимает любой формат строки:

>>> data = StringIO("1 2 3\n 4 5 6")
>>> np.genfromtxt(data, dtype=(int, float, int), defaultfmt="var_%02i")
array([(1, 2.0, 3), (4, 5.0, 6)],
      dtype=[('var_00', '<i8'), ('var_01', '<f8'), ('var_02', '<i8')])

Примечание

Нужно помнить, что defaultfmt используется только в том случае, если некоторые имена ожидаются, но не определены.

Проверка имен

Массивы NumPy со структурированным типом данных также можно рассматривать как recarray, где к полю можно получить доступ, как если бы оно было атрибутом. По этой причине нам необходимо убедиться, что имя поля не содержит пробелов или недопустимых символов, или что оно не соответствует имени стандартного атрибута (например, size или shape), что сбивает с толку интерпретатор. genfromtxt принимает три необязательных аргумента, которые обеспечивают более точный контроль над именами:

deletechars

Указывает строку, объединяющую все символы, которые должны быть удалены из имени. По умолчанию недопустимые символы — ~!@#$%^&*()-=+~\|]}[{';: /?.>,<.

excludelist

Указывает список имен, которые необходимо исключить, например, return, file, print… Если одно из входных имён входит в этот список, к нему будет добавлен символ нижнего подчеркивания ('_').

case_sensitive

Определяет, должны ли имена быть регистрозависимыми (case_sensitive=True), преобразовываться в верхний регистр (case_sensitive=False или case_sensitive='upper'), или в нижний регистр (case_sensitive='lower').

Настройка преобразования

Аргумент converters

Обычно определение типа данных достаточно для определения того, как последовательность строк должна быть преобразована. Однако иногда может потребоваться дополнительный контроль. Например, мы можем убедиться, что дата в формате YYYY/MM/DD преобразуется в объект datetime, или что строка типа xx% правильно преобразуется в число с плавающей запятой от 0 до 1. В таких случаях необходимо определить функции преобразования с аргументом converters.

Значение этого аргумента обычно является словарем, где ключами являются индексы столбцов или имена столбцов, а значениями — функции преобразования. Эти функции преобразования могут быть как фактическими функциями, так и лямбда-функциями. В любом случае они должны принимать только строку в качестве входных данных и возвращать только один элемент желаемого типа.

В следующем примере второй столбец преобразуется из строки, представляющей процент, в число с плавающей запятой от 0 до 1:

>>> convertfunc = lambda x: float(x.strip(b"%"))/100.
>>> data = u"1, 2.3%, 45.\n6, 78.9%, 0"
>>> names = ("i", "p", "n")
>>> # General case .....
>>> np.genfromtxt(StringIO(data), delimiter=",", names=names)
array([(1., nan, 45.), (6., nan, 0.)],
      dtype=[('i', '<f8'), ('p', '<f8'), ('n', '<f8')])

Следует помнить, что по умолчанию dtype=float. Таким образом, для второго столбца ожидается число с плавающей запятой. Однако строки ' 2.3%' и ' 78.9%' нельзя преобразовать в число с плавающей запятой, и вместо этого мы получаем np.nan. Теперь давайте используем преобразователь:

>>> # Converted case ...
>>> np.genfromtxt(StringIO(data), delimiter=",", names=names,
...               converters={1: convertfunc})
array([(1.0, 0.023, 45.0), (6.0, 0.78900000000000003, 0.0)],
      dtype=[('i', '<f8'), ('p', '<f8'), ('n', '<f8')])

Того же результата можно достичь, используя имя второго столбца ("p") в качестве ключа вместо его индекса (1):

>>> # Using a name for the converter ...
>>> np.genfromtxt(StringIO(data), delimiter=",", names=names,
...               converters={"p": convertfunc})
array([(1.0, 0.023, 45.0), (6.0, 0.78900000000000003, 0.0)],
      dtype=[('i', '<f8'), ('p', '<f8'), ('n', '<f8')])

Преобразователи также могут использоваться для предоставления значения по умолчанию для отсутствующих записей. В следующем примере преобразователь convert преобразует очищенную строку в соответствующее число с плавающей запятой или в -999, если строка пуста. Необходимо явно очистить строку от пробелов, так как это не делается по умолчанию:

>>> data = u"1, , 3\n 4, 5, 6"
>>> convert = lambda x: float(x.strip() or -999)
>>> np.genfromtxt(StringIO(data), delimiter=",",
...               converters={1: convert})
array([[   1., -999.,    3.],
       [   4.,    5.,    6.]])

Использование отсутствующих и заполняемых значений

В наборе данных, который мы пытаемся импортировать, некоторые записи могут отсутствовать. В предыдущем примере мы использовали преобразователь для преобразования пустой строки в число с плавающей запятой. Однако пользовательские преобразователи могут быстро стать неудобными для управления.

Функция genfromtxt предоставляет два других дополнительных механизма: аргумент missing_values используется для распознавания отсутствующих данных, а второй аргумент, filling_values, используется для обработки этих отсутствующих данных.

missing_values

По умолчанию любая пустая строка отмечается как отсутствующая. Мы также можем рассмотреть более сложные строки, такие как "N/A" или "???", для представления отсутствующих или недопустимых данных. Аргумент missing_values принимает три типа значений:

строка или строка, разделенная запятыми

Эта строка будет использоваться как маркер отсутствующих данных для всех столбцов

последовательность строк

В этом случае каждый элемент ассоциируется со столбцом в порядке.

словарь

Значения словаря — это строки или последовательности строк. Соответствующие ключи могут быть индексами столбцов (целые числа) или именами столбцов (строки). Кроме того, специальный ключ None может использоваться для определения значения по умолчанию, применимого ко всем столбцам.

filling_values

Мы знаем, как распознавать отсутствующие данные, но нам все еще нужно предоставить значение для этих отсутствующих записей. По умолчанию это значение определяется из ожидаемого типа данных в соответствии с этой таблицей:

Ожидаемый тип

Значение по умолчанию

bool

False

int

-1

float

np.nan

complex

np.nan+0j

string

'???'

Мы можем получить более точный контроль над преобразованием отсутствующих значений с помощью необязательного аргумента filling_values. Как и missing_values, этот аргумент принимает различные значения:

единичное значение

Это будет значением по умолчанию для всех столбцов

последовательность значений

Каждое значение будет значением по умолчанию для соответствующего столбца

словарь

Каждый ключ может быть индексом столбца или именем столбца, а соответствующее значение должно быть одиночным объектом. Мы можем использовать специальный ключ None для определения значения по умолчанию для всех столбцов.

В следующем примере предполагается, что отсутствующие значения помечены как "N/A" в первом столбце и "???" в третьем столбце. Мы хотим преобразовать эти отсутствующие значения в 0, если они встречаются в первом и втором столбцах, и в -999, если они встречаются в последнем столбце:

>>> data = u"N/A, 2, 3\n4, ,???"
>>> kwargs = dict(delimiter=",",
...               dtype=int,
...               names="a,b,c",
...               missing_values={0:"N/A", 'b':" ", 2:"???"},
...               filling_values={0:0, 'b':0, 2:-999})
>>> np.genfromtxt(StringIO(data), **kwargs)
array([(0, 2, 3), (4, 0, -999)],
      dtype=[('a', '<i8'), ('b', '<i8'), ('c', '<i8')])

usemask

Мы также можем отслеживать случаи отсутствующих данных, создавая маску булевых значений, где True соответствуют отсутствующим данным, а False — иным. Для этого нужно установить необязательный аргумент usemask в значение True (по умолчанию False). Тогда выходной массив будет MaskedArray.

Функции-заменители

Помимо genfromtxt, модуль numpy.lib.io предоставляет несколько удобных функций, полученных из genfromtxt. Эти функции работают так же, как и исходные, но имеют другие значения по умолчанию.

recfromtxt

Возвращает стандартный numpy.recarray (если usemask=False) или массив MaskedRecords (если usemaske=True). Тип данных по умолчанию — dtype=None, что означает, что типы каждого столбца будут определяться автоматически.

recfromcsv

Аналогично recfromtxt, но со значением по умолчанию delimiter=",".

© 2005–2020 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/1.19/user/basics.io.genfromtxt.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API