Spec-Zone.ru › NumPy 1.18

Импортирование данных с помощью 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. Результатом будет двумерный массив с заданным типом, если не было присвоено имя каждому столбцу с помощью аргумента 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.18/user/basics.io.genfromtxt.html

Spec-Zone.ru

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