Попытка воссоздать ваш случай:
In [182]: a,b,c = 0,1,2
In [183]: metric1, metric2 = 100,200
In [186]: data = [
...: [ 1, a, [metric1, metric2] ],
...: [ 1, b, [metric1, metric2] ],
...: [ 2, b, [metric1, metric2] ],
...: [ 2, c, [metric1, metric2] ],
...: [ 3, a, [metric1, metric2] ],
...: [ 3, c, [metric1, metric2] ],
...: ]
In [187]:
In [187]: data
Out[187]:
[[1, 0, [100, 200]],
[1, 1, [100, 200]],
[2, 1, [100, 200]],
[2, 2, [100, 200]],
[3, 0, [100, 200]],
[3, 2, [100, 200]]]
In [189]: data = np.array(data,object)
In [190]: rows, row_pos = np.unique(data[:, 0], return_inverse=True)
...: cols, col_pos = np.unique(data[:, 1], return_inverse=True)
...: pivot_table = np.zeros((len(rows), len(cols)), dtype=object)
In [191]: pivot_table
Out[191]:
array([[0, 0, 0],
[0, 0, 0],
[0, 0, 0]], dtype=object)
In [192]: pivot_table[row_pos, col_pos] = data[:, 2]
In [193]: pivot_table
Out[193]:
array([[list([100, 200]), list([100, 200]), 0],
[0, list([100, 200]), list([100, 200])],
[list([100, 200]), 0, list([100, 200])]], dtype=object)
In [194]: pivot_table[row_pos, col_pos]
Out[194]:
array([list([100, 200]), list([100, 200]), list([100, 200]),
list([100, 200]), list([100, 200]), list([100, 200])], dtype=object)
In [195]: _.shape
Out[195]: (6,)
In [196]: data[:,2].shape
Out[196]: (6,)
Это назначение работает между исходной формой (и dtype), совпадающей с целевой (6,).
In [197]: mask = pivot_table==0
In [198]: mask
Out[198]:
array([[False, False, True],
[ True, False, False],
[False, True, False]])
In [199]: pivot_table[mask]
Out[199]: array([0, 0, 0], dtype=object)
In [200]: pivot_table[mask] = [0,0]
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-200-83e0a7422802> in <module>()
----> 1 pivot_table[mask] = [0,0]
ValueError: NumPy boolean array indexing assignment cannot assign 2 input values to the 3 output values where the mask is true
Другое сообщение об ошибке (другая версия NumPy?), но это говорит, что я пытаюсь положить 2 значения в 3 слота.Он не обрабатывает [0,0]
как отдельный элемент, а как 2.
Нет проблем с назначением скалярного элемента:
In [203]: pivot_table[mask] = None
In [204]: pivot_table
Out[204]:
array([[list([100, 200]), list([100, 200]), None],
[None, list([100, 200]), list([100, 200])],
[list([100, 200]), None, list([100, 200])]], dtype=object)
В прошлом я имелУспешное использование frompyfunc
для создания массивов dtype объектов.Определите небольшую функцию.Я мог бы проверить на 0 или тип, но так как я уже вставил None, давайте проверим это:
In [205]: def fun(x):
...: if x is None: return [0,0]
...: return x
Примените его к каждому элементу pivot_table
, создав новый массив.
In [230]: arr1 = np.frompyfunc(fun,1,1)(pivot_table)
In [231]: arr1
Out[231]:
array([[list([100, 200]), list([100, 200]), list([0, 0])],
[list([0, 0]), list([100, 200]), list([100, 200])],
[list([100, 200]), list([0, 0]), list([100, 200])]], dtype=object)
Другой подход, давайте попробуем назначить список списков:
In [240]: pivot_table[mask] = [[0,0] for _ in range(3)]
TypeError: NumPy boolean array indexing assignment requires a 0 or 1-dimensional input, input has 2 dimensions
Но если я попробую то же самое с where
, это сработает:
In [241]: pivot_table[np.where(mask)] = [[0,0] for _ in range(3)]
In [242]: pivot_table
Out[242]:
array([[list([100, 200]), list([100, 200]), list([0, 0])],
[list([0, 0]), list([100, 200]), list([100, 200])],
[list([100, 200]), list([0, 0]), list([100, 200])]], dtype=object)
С where
это больше похоже на ваше первоначальное присвоение pivot_table
.
In [243]: np.where(mask)
Out[243]: (array([0, 1, 2]), array([2, 0, 1]))
У индексации этого массива все еще могут быть проблемы с широковещательной передачей,
In [244]: pivot_table[np.where(mask)] = [0,0]
ValueError: cannot copy sequence with size 2 to array axis with dimension 3
Обычно логическая маскаИндекс ведет себя как эквивалентное np.where(mask)
индексирование, но, очевидно, здесь взаимодействие объекта dtype и вещание путаются с логическим индексированием.
Out[231]
по-прежнему (3,3) массив,хотя все элементы лен 2 списков.Чтобы превратить его в числовой массив, мы должны сделать что-то вроде:
In [248]: p = np.stack(pivot_table.ravel()).reshape(3,3,2)
In [249]: p
Out[249]:
array([[[100, 200],
[100, 200],
[ 0, 0]],
[[ 0, 0],
[100, 200],
[100, 200]],
[[100, 200],
[ 0, 0],
[100, 200]]])
np.concatenate
(и *stack
версии) могут объединять списки в массив, но он должен начинаться со списка или плоскогомассив, следовательно, необходимость в ravel и reshape.
np.array(pivot_table.tolist())
также работает.
Если бы вместо этого вы построили структурированный массив данных (предполагая, что значения metric
являются числовыми):
In [265]: data1 = np.array([tuple(x.tolist()) for x in data],'i,i,2i')
In [266]: data1
Out[266]:
array([(1, 0, [100, 200]), (1, 1, [100, 200]), (2, 1, [100, 200]),
(2, 2, [100, 200]), (3, 0, [100, 200]), (3, 2, [100, 200])],
dtype=[('f0', '<i4'), ('f1', '<i4'), ('f2', '<i4', (2,))])
In [267]: data1['f2']
Out[267]:
array([[100, 200],
[100, 200],
[100, 200],
[100, 200],
[100, 200],
[100, 200]], dtype=int32)
эти значения могут быть присвоены 3d pivot_table:
In [268]: p = np.zeros((len(rows), len(cols),2),int)
In [269]: p[row_pos, col_pos]=data1['f2']
С массивом fillvalue
, который определил Пол Панцер, ваше первоначальное маскированное назначение работает:
In [322]: fillvalue = np.empty((), 'O')
...: fillvalue[()] = [0, 0]
...:
In [323]: fillvalue
Out[323]: array(list([0, 0]), dtype=object)
In [324]: mask
Out[324]:
array([[False, False, True],
[ True, False, False],
[False, True, False]])
In [325]: pivot_table[mask] = fillvalue
Его full
делает np.copyto(a, fill_value, casting='unsafe')
. Наше маскированное задание можно записать так: np.copyto(pivot_table, fillvalue, where=mask)