2016-04-15 3 views
2

Как написать класс оболочки, который заставляет это работать?kwarg-splatting numpy array

def foo(a, b): 
    print a 

data = np.empty(20, dtype=[('a', np.float32), ('b', np.float32)]) 

data = my_magic_ndarray_subclass(data) 

foo(**data[0]) 

Некоторые более фона:

Я имел пару функций, как это, что я хотел векторизации:

def start_the_work(some_arg): 
    some_calculation = ... 
    something_else = ... 

    cost = some_calculation * something_else 

    return cost, dict(
     some_calculation=some_calculation, 
     some_other_calculation=some_other_calculation 
    ) 

def finish_the_work(some_arg, some_calculation, some_other_calculation): 
    ... 

С намерением, что start_the_work вызывается с кучей различные аргументы, а затем элемент с наименьшей стоимостью завершен. Многие из тех же расчетов используются обеими функциями, поэтому словарь и kwarg-splatting используется для передачи этих результатов:

def run(): 
    best, best_cost, continuation = min(
     ((some_arg,) + start_the_work(some_arg) 
     for some_arg in [1, 2, 3, 4]), 
     key=lambda t: t[1] # cost 
    ) 
    return finish_the_work(best, **continuation) 

Один из способов я могу векторизации их следующим образом:

def start_the_work(some_arg): 
    some_calculation = ... 
    something_else = ... 

    cost = some_calculation * something_else 

    continuation = np.empty(cost.shape, dtype=[ 
     ('some_calculation', np.float32), 
     ('some_other_calculation', np.float32) 
    ]) 
    continuation['some_calculation'] = some_calculation 
    continuation['some_other_calculation'] = some_other_calculation 

    return cost, continuation 

Но, несмотря на то, что он выглядит как словарь, continuation не может быть kwarg-splatted.

+0

'Foo (* данные [0])' работает, потому что запись структурированного массива ведет себя (для целей итерации) как кортеж. – hpaulj

+0

Какое поведение должен выполнять класс как '** kwarg'? – Eric

+0

Кажется, ответ на этот вопрос: 'keys()' и '__getitem__' – Eric

ответ

2

Это может быть не совсем то, что вы хотите, но оборачивать массив в панд DataFrame позволяет что-то вроде этого:

import pandas as pd 

def foo(a, b): 
    print(a) 

data = np.empty(20, dtype=[('a', np.float32), ('b', np.float32)]) 

data = pd.DataFrame(data).T 

foo(**data[0]) 
# 0.0 

Обратите внимание, что dataframe транспонируется, поскольку первичный индекс панд является столбец, а не ряд.

+0

Это стоит знать, спасибо! Я не думаю, что панды действительно тоже подходят для большей части того, что я делаю, хотя – Eric

1

Вы думаете, что, поскольку поля структурированного массива могут быть доступны по имени, они могут передаваться как элементы словаря?

In [26]: x=np.ones((3,),dtype='i,f,i') 

In [27]: x 
Out[27]: 
array([(1, 1.0, 1), (1, 1.0, 1), (1, 1.0, 1)], 
     dtype=[('f0', '<i4'), ('f1', '<f4'), ('f2', '<i4')]) 

In [28]: x['f0'] 
Out[28]: array([1, 1, 1]) 

Преобразование его в словарь работы:

In [29]: dd={'f0':x['f0'], 'f1':x['f1'], 'f2':x['f2']} 

In [30]: def foo(**kwargs): 
    ...:  print kwargs 
    ...:  

In [31]: foo(**dd) 
{'f0': array([1, 1, 1]), 'f1': array([ 1., 1., 1.], dtype=float32), 'f2': array([1, 1, 1])} 

In [32]: foo(**x) # the array itself won't work 
... 
TypeError: foo() argument after ** must be a mapping, not numpy.ndarray 

Или с помощью словаря понимания:

In [34]: foo(**{name:x[name] for name in x.dtype.names}) 
{'f0': array([1, 1, 1]), 'f1': array([ 1., 1., 1.], dtype=float32), 'f2': array([1, 1, 1])} 

**kwargs может зависеть от объекта, имеющего метод .keys(). Массив нет.


элемент структурированного массива np.void:

In [163]: a=np.array([(1,2),(3,4)],dtype='i,i') 

In [164]: a[0] 
Out[164]: (1, 2) 

In [165]: type(a[0]) 
Out[165]: numpy.void 

Он имеет DTYPE и имена:

In [166]: a[0].dtype.names 
Out[166]: ('f0', 'f1') 

In [167]: [{k:b[k] for k in b.dtype.names} for b in a] 
Out[167]: [{'f0': 1, 'f1': 2}, {'f0': 3, 'f1': 4}] 

с подклассу массива, view имеет этот keys:

class spArray(np.ndarray): 
    def keys(self): 
     return self.dtype.names 

In [171]: asp=a.view(spArray) 

In [172]: asp 
Out[172]: 
spArray([(1, 2), (3, 4)], 
     dtype=[('f0', '<i4'), ('f1', '<i4')]) 

In [173]: asp.keys() 
Out[173]: ('f0', 'f1') 

Другие способы построения этого класса не работают (т. прямой вызов) - это часть сложности подкласса ndarray.

def foo(**kwargs): 
    print kwargs 

In [175]: foo(**asp) 
{'f0': spArray([1, 3]), 'f1': spArray([2, 4])} 

In [176]: foo(**asp[0]) 
... 
TypeError: foo() argument after ** must be a mapping, not numpy.void 

In [177]: foo(**asp[[0]]) 
{'f0': spArray([1]), 'f1': spArray([2])} 

splatting массив или массив 1 элемент, извлеченный из его работы, но элемент, в этом случае np.void элемент не делает. Он не имеет метода key.

Я пробовал подклассификацию np.void, как вы делали массив; он принимает определение. Но я не могу найти способ создания такого объекта.

+0

Проблема в том, что у меня есть класс baes api, который ожидает тип kwarg-splattable, и этот код должен продолжать работать, когда возвращается словарь. Я бы предпочел не использовать специальные массивы numpy там, и вместо этого я задаюсь вопросом, могу ли я добавить kwargs-splatting с 'data.view (my_class)' – Eric

+0

Неловко изменять класс 'ndarray' с помощью нового или измененного метода. Но вы можете реализовать этот массив для преобразования словаря другими способами - как автономная функция, метод класса обертки и т. Д. – hpaulj

0

Это почти работает:

class SplattableArray(np.ndarray): 
    def keys(self): 
     return self.dtype.names 

data = np.empty(20, dtype=[('a', np.float32), ('b', np.float32)]) 
data_splat = data.view(SplattableArray) 

def foo(a, b): 
    return a*b 

foo(**data_splat) # works! 
foo(**data_splat[0]) # doesn't work :(

Если мы готовы быть страшные люди, то это работает:

from forbiddenfruit import curse 
import numpy as np 

def keys(obj): 
    return obj.dtype.names 

curse(np.void, 'keys', keys) 
curse(np.ndarray, 'keys', keys) 

data = np.empty(10, dtype='i,i') 
def foo(**kwargs): 
    return kwargs 

foo(**data[0]) 
foo(**data) 
+0

Я добавил некоторые изменения этого изменения в свой ответ. – hpaulj

+0

@hpaulj: Если я согласен на monkeypatch, у меня есть решение – Eric