В науке о данных требуются быстрые вычисления и трансформация данных. Родные NumPy объекты в Python имеют такое преимущество над обычными программными объектами. Они работают так же просто, как чтение числовых данных из файла на диске. Мы продемонстрируем преимущество с несколькими строками кода.

Вступление

Numpy это сокращение от Numerical Python(числовой Python). Это фундаментальный пакет, необходимый для высокопроизводительных научных вычислений и анализа данных в экосистеме Python. Для почти всех высокоуровневых инструментов, таких как Pandas и scikit-learn был использован этот пакет. TensorFlow использует массивы NumPy как фундаментальный строительный блок, поверх которого они строят свои Tensor-объекты и графики потоков для глубоко изучающих заданий(что делает сложным использование линейных алгоритмов из алгебры для большого списка чисел).

Много статей было написано для демонстрации преимущества NumPy-массивов над классическими списками Python. Вы будете часто сталкиваться с этими утверждениеми, когда читаете о науке о данных, машинном изучении или просто Python-топики. В качестве аргументов обозначают, что NumPy быстрее за счёт своей векторной реализации и за счёт того, что многие основные его подпрограммы написаны на C (основанным на фреймворке CPython). И это в самом деле так(это статья — прекрасная демонстрация различных вариантов того, как можно работать с NumPy. Можно даже написать скелет для C с помощью NumPy API). Массивы NumPy представляют из себя плотно упакованные массивы однородного типа. Списки Python, напротив, представляют собой массивы указателей на объекты, даже если они все одного типа. Вы получаете определение локальности ссылки.

В одной из моих наиболее цитируемых статей на Towards Data Science я демонстрировал преимущество использования векторизированных операций NumPy по сравнению с традиционным для программирования циклом for.

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

Загвоздка в том, что вам нужно прочитать данные традиционным способом в первый раз и создать внутри памяти NumPy-объект ndarray. Но если для чтения одних и тех же числовых данных один и тот же CSV-файл, то имеет смысл хранить ndarray в npy файле вместо того, чтобы читать его снова и снова из оригинального CSV-файла.

Что такое .npy-файл?

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

Формат должен иметь следующие возможности:

  • Представлять все NumPy-массивы, в том числе вложенные массивы записей и массивы объектов.
  • Представлять данные в своей естественной бинарной форме.
  • Содержаться в одном файле.
  • Хранить всю необходимую информацию о форме и типе данных для правильно восстановления массива даже на другой машине с другой архитектурой. Оба массива little-endian и big-endian должны поддерживаться. Файл с числами little-endian должен выдавать little-endian массив на любой машине, которая читает файл. Файлы должны быть описаны в соответствии со своими реальными размерами. Например, если машина с 64-битным C «long int» выписывает массив с «long int», то машина с 32-битным C «long int» должна принести массив с 64-битными числами.
  • Обратная разработка. Наборы данных часто живут дольше, чем программы, которые их создали. Компетентный разработчик должен быть способен разработать решение в предпочитаемом ему языке программирования для чтения большинства NPY-файлов, которые он получил, не имея толковой документации.
  • Разрешить отображение карт памяти.

Демо-использование простого кода

Как всегда, вы можете скачать шаблон кода в Notepad на странице моего Guthub-репозитория. Здесь я покажу основной отрывок из кода.

Для начала обычный метод чтения SCV-файла как список и конвертация его в ndarray.

import numpy as np
import time

# 1 million samples
n_samples=1000000

# Write random floating point numbers as string on a local CSV file
with open('fdata.txt', 'w') as fdata:
    for _ in range(n_samples):
        fdata.write(str(10*np.random.random())+',')

# Read the CSV in a list, convert to ndarray (reshape just for fun) and time it
t1=time.time()
with open('fdata.txt','r') as fdata:
    datastr=fdata.read()
lst = datastr.split(',')
lst.pop()
array_lst=np.array(lst,dtype=float).reshape(1000,1000)
t2=time.time()

print(array_lst)
print('\nShape: ',array_lst.shape)
print(f"Time took to read: {t2-t1} seconds.")

>> [[0.32614787 6.84798256 2.59321025 ... 5.02387324 1.04806225 2.80646522]
 [0.42535168 3.77882315 0.91426996 ... 8.43664343 5.50435042 1.17847223]
 [1.79458482 5.82172793 5.29433626 ... 3.10556071 2.90960252 7.8021901 ]
 ...
 [3.04453929 1.0270109  8.04185826 ... 2.21814825 3.56490017 3.72934854]
 [7.11767505 7.59239626 5.60733328 ... 8.33572855 3.29231441 8.67716649]
 [4.2606672  0.08492747 1.40436949 ... 5.6204355  4.47407948 9.50940101]]

>> Shape:  (1000, 1000)
>> Time took to read: 1.018733024597168 seconds.

Это было для первого чтения, которое вам нужно выполнить в любом случае. Но если вы хотите использовать один и тот же набор данных большое количество раз, тогда не забудьте после окончания вашего процесса сохранить ndarray-объект в формат .ndy.

np.save('fnumpy.npy', array_lst)

Потому что если вы это сделаете, то следующее прочтение данных с диска будет невероятно быстрым!

t1=time.time()
array_reloaded = np.load('fnumpy.npy')
t2=time.time()

print(array_reloaded)
print('\nShape: ',array_reloaded.shape)
print(f"Time took to load: {t2-t1} seconds.")

>> [[0.32614787 6.84798256 2.59321025 ... 5.02387324 1.04806225 2.80646522]
 [0.42535168 3.77882315 0.91426996 ... 8.43664343 5.50435042 1.17847223]
 [1.79458482 5.82172793 5.29433626 ... 3.10556071 2.90960252 7.8021901 ]
 ...
 [3.04453929 1.0270109  8.04185826 ... 2.21814825 3.56490017 3.72934854]
 [7.11767505 7.59239626 5.60733328 ... 8.33572855 3.29231441 8.67716649]
 [4.2606672  0.08492747 1.40436949 ... 5.6204355  4.47407948 9.50940101]]

>> Shape:  (1000, 1000)
>> Time took to load: 0.009010076522827148 seconds.

И даже не важно, хотите ли вы загрузить данные в каком-то другом виде…

t1=time.time()
array_reloaded = np.load('fnumpy.npy').reshape(10000,100)
t2=time.time()

print(array_reloaded)
print('\nShape: ',array_reloaded.shape)
print(f"Time took to load: {t2-t1} seconds.")

>> [[0.32614787 6.84798256 2.59321025 ... 3.01180325 2.39479796 0.72345778]
 [3.69505384 4.53401889 8.36879084 ... 9.9009631  7.33501957 2.50186053]
 [4.35664074 4.07578682 1.71320519 ... 8.33236349 7.2902005  5.27535724]
 ...
 [1.11051629 5.43382324 3.86440843 ... 4.38217095 0.23810232 1.27995629]
 [2.56255361 7.8052843  6.67015391 ... 3.02916997 4.76569949 0.95855667]
 [6.06043577 5.8964256  4.57181929 ... 5.6204355  4.47407948 9.50940101]]

>> Shape:  (10000, 100)
>> Time took to load: 0.010006189346313477 seconds.

Всё равно выяснится, что, по крайней мере в этом конкретном случае, размер файла на диске тоже будет меньше для .npy формата.

Итог

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

Прочитать больше информации об этом формате вы можете здесь.

Перевод статьи Tirthajyoti Sarkar: «Why you should start using .npy file more often…«