Импорт данных с genfromtxt#

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

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

Примечание

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

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

Определение ввода#

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

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

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

The delimiter аргумент#

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

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

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

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

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

>>> data = "  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 = "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.]])

The autostrip аргумент#

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

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

The comments аргумент#

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

>>> data = """#
... # 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.]])

Примечание

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

Пропуск строк и выбор столбцов#

The usecols аргумент#

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

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

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

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

>>> data = "1 2 3\n4 5 6"
>>> np.genfromtxt(StringIO(data),
...               names="a, b, c", usecols=("a", "c"))
array([(1., 3.), (4., 6.)], dtype=[('a', '
>>> np.genfromtxt(StringIO(data),
...               names="a, b, c", usecols=("a, c"))
    array([(1., 3.), (4., 6.)], dtype=[('a', '

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

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

  • один тип, такой как dtype=float. Выходные данные будут двумерными с заданным dtype, если только имя не было связано с каждым столбцом с использованием names аргумент (см. ниже). Обратите внимание, что dtype=float 8.1 genfromtxt.

  • последовательность типов, например dtype=(int, float, float).

  • строка, разделенная запятыми, например dtype="i4,f8,|U3".

  • словарь с двумя ключами 'names' и 'formats'.

  • последовательность кортежей (name, type), такие как dtype=[('A', int), ('B', float)].

  • существующий numpy.dtype объект.

  • специальное значение None. В этом случае тип столбцов будет определён из самих данных (см. ниже).

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

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

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

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

The names аргумент#

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

>>> 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', '

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

>>> data = StringIO("1 2 3\n 4 5 6")
>>> np.genfromtxt(data, names="A, B, C")
array([(1., 2., 3.), (4., 5., 6.)],
      dtype=[('A', '

В примере выше мы использовали тот факт, что по умолчанию, 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., 2., 3.), (4., 5., 6.)],
      dtype=[('a', '

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

>>> 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., 3), (4, 5., 6)],
      dtype=[('A', '

The defaultfmt аргумент#

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

>>> data = StringIO("1 2 3\n 4 5 6")
>>> np.genfromtxt(data, dtype=(int, float, int))
array([(1, 2., 3), (4, 5., 6)],
      dtype=[('f0', '

Аналогично, если мы не дадим достаточно имен для соответствия длине dtype, недостающие имена будут определены с помощью этого шаблона по умолчанию:

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

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

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

Примечание

Нам нужно помнить, что 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').

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

The converters аргумент#

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

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

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

>>> convertfunc = lambda x: float(x.strip("%"))/100.
>>> data = "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', '

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

>>> # Converted case ...
>>> np.genfromtxt(StringIO(data), delimiter=",", names=names,
...               converters={1: convertfunc})
array([(1., 0.023, 45.), (6., 0.789, 0.)],
      dtype=[('i', '

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

>>> # Using a name for the converter ...
>>> np.genfromtxt(StringIO(data), delimiter=",", names=names,
...               converters={"p": convertfunc})
array([(1., 0.023, 45.), (6., 0.789, 0.)],
      dtype=[('i', '

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

>>> data = "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.]])

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

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

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

missing_values#

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

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

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

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

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

словарь

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

filling_values#

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

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

По умолчанию

bool

False

int

-1

float

np.nan

complex

np.nan+0j

string

'???'

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

единственное значение

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

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

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

словарь

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

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

>>> data = "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', '

usemask#

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