Spec-Zone.ru › NumPy 1.20

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

recfromtxt

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

recfromcsv

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

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

Spec-Zone.ru

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