Spec-Zone.ru › NumPy 1.21

Импорт данных с помощью 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, используется для обработки этих отсутствующих данных.

Отсутствующие значения

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

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

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

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

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

словарь

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

Заполнение значений

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

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

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

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.npyio предоставляет несколько удобных функций, полученных из genfromtxt. Эти функции работают так же, как и исходная, но имеют другие значения по умолчанию.

recfromtxt

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

recfromcsv

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

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

Spec-Zone.ru

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