Использование памяти строкового массива NumPy намного больше, чем массива объектов - PullRequest
0 голосов
/ 27 июня 2019

Я все еще новичок в Numpy, возился с dtypes в Numpy и обнаружил, что dtype, специфичный для строк, или U, использует больше места в памяти, чем тип объекта.Код, иллюстрирующий этот факт, приведен ниже:

size= 100000
half_size = size//2

ind1 = np.arange(half_size)*2+1
ind2 = np.arange(half_size)*2

X = np.empty(size, dtype = 'object')

X[ind1] = 'smile'
X[ind2] = 'smile2'

W = np.empty(size, dtype = 'U6')
W[ind1] = 'smile'
W[ind2] = 'smile2'

print(X.nbytes)
print(W.nbytes)

Результат следующий:

800000
2400000

Мои вопросы следующие:

1) Почемуэто случилось?Почему dtype = 'U6' занимает в 3 раза больше памяти, чем dtype = object

2) Есть ли способ создать массив с пустыми строками, который занимает меньше памяти, чем объект dtype = object?

Заранее спасибо

РЕДАКТИРОВАТЬ: Я хотел бы объяснить, что мой пост не является дубликатом другой пост , потому что мой пост об использовании памяти, ив другом посте ничего не говорится об использовании памяти относительно dtype = 'U' vs dtype = 'object'

EDIT2: хотя я уже узнал что-то новое из другого поста , к сожалению, другогопост не отвечает на мой вопрос, потому что мой пост посвящен использованию памяти, а другой пост не упоминает ничего об использовании памяти относительно dtype = 'U' vs dtype = 'object'

Ответы [ 2 ]

2 голосов
/ 27 июня 2019

sys.getsizeof - это один из способов проверки использования памяти, хотя вы должны использовать его с умом, понимая, что именно он измеряет. Для массивов это работает довольно хорошо.

Массив без каких-либо элементов:

In [28]: sys.getsizeof(np.array([],'U6'))                                                            
Out[28]: 96
In [29]: sys.getsizeof(np.array(['smile','smile1'],'U6'))                                            
Out[29]: 144
In [30]: sys.getsizeof(np.array(['smile','smile1'],'S6'))                                            
Out[30]: 108

При 2 строках 'U6' размер увеличивается на 48,4 байта / символ * 2 элемента * 6 символов на элемент

При использовании типа d для байтовой строки (по умолчанию для Py2) переход составляет 12, 2 * 6.

bytestring более компактен, но обратите внимание на дисплей:

In [31]: np.array(['smile','smile1'],'S6')                                                           
Out[31]: array([b'smile', b'smile1'], dtype='|S6')

Для объекта dtype:

In [32]: sys.getsizeof(np.array(['smile','smile1'],object))                                          
Out[32]: 112

Это 16 байтов - 2 * 8

Но добавьте к этому размер строк Python, дополнительные 133 байта

In [33]: sys.getsizeof('smile')                                                                      
Out[33]: 78
In [34]: sys.getsizeof('smile1')                                                                     
Out[34]: 55

и для строк байтов:

In [36]: sys.getsizeof(b'smile')                                                                     
Out[36]: 38
In [37]: sys.getsizeof(b'smile1')                                                                    
Out[37]: 39

Обратите внимание, что когда я добавляю символ байта, размер увеличивается на 1. Но когда я добавляю символ юникода, размер фактически уменьшается. Размер юникодных строк сложнее предсказать. Я думаю, что он может выделять до 4 байтов на символ, но фактическое число зависит от символа и кодировки. Обычно мы не пытаемся микроуправлять обработкой строк в Python. (Кроме того, я считаю, что в Python есть какой-то строковый кеш.)

Но когда вы назначаете

X[ind1] = 'smile'
X[ind2] = 'smile2'

в случае объекта вы создаете две строки Python и назначаете ссылки (указатели) на массив. Таким образом, используется память массива (1000 ... * 8 байтов) плюс 133 байта для этих двух строк.

В случае «U6» каждый элемент занимает 4 * 6 байтов, независимо от того, является ли он «smile» или «smile1» (или «s»). Каждый элемент массива использует одно и то же пространство независимо от того, нужен ли он всем для представления строки или нет.

Обычно строки не имеют numpy силы. Использование памяти типа 'U' или 'S' в порядке, когда строки имеют одинаковый размер, но менее оптимально, если строки различаются по длине, повторяются и / или имеют юникод. numpy не выполняет большую часть собственной обработки строк. Функции np.char являются лишь тонкими обертками строковых методов Python.

pandas решил использовать object dtype вместо строки dtypes.

1 голос
/ 27 июня 2019

Если вы проверите размер каждого типа данных в памяти, вы получите:

import numpy as np

dt = np.dtype('object')
print('object = %i bytes' % dt.itemsize)

dt = np.dtype('U6')
print('U6 = %i bytes' % dt.itemsize)

Вывод:

object = 8 bytes
U6 = 24 bytes
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...