Числовые эквиваленты функций массива Ruby - PullRequest
0 голосов
/ 15 мая 2019

Я работаю над заданием, чтобы попрактиковаться в предварительной обработке данных, в данном случае с биннингом равной ширины, но я не знаком с этими крошечными функциями, поэтому мой код на Python выглядит довольно уродливо:

def eq_width_bin(data, bins):
  bin_edge = np.linspace(np.min(data), np.max(data), bins+1)
  bin_edge[-1] += 1
  re  = []
  for i in data:
    for j in bin_edge:
      if i < j:
        re.append(int(np.argwhere(bin_edge==j))-1)
        break
  return re

data = np.array([80, 95, 70, 30, 20, 10, 75, 65, 98, 103, 130, 70])
print("After equal width binning:\n{}".format(eq_width_bin(data, 3)))

однако в ruby ​​я могу сделать это менее чем с 10 строками (несмотря на то, что это довольно медленно):

def eq_width_bin(data, bins)
  bin_edge = bins.times.collect{|i| data.min + (data.max - data.min) / bins * i} << data.max + 1
  return data.collect{|i| bin_edge.index{|j| i < j} - 1}
end
data = [80, 95, 70, 30, 20, 10, 75, 65, 98, 103, 130, 70]
puts "After equal width binning:\n#{eq_width_bin(data, 3)}"

Я часто использую .select .collect .inject .sort_by для работы с массивом в ruby, так есть ли какие-нибудь непонятные функции, которые я могу использовать, чтобы "украсить" мой код Python выше? (Особенно зная, что встроенные функции numpy намного быстрее, чем в pyhton)

1 Ответ

0 голосов
/ 15 мая 2019

Изначально это выглядело как bincount или histogram, но выводом являются ячейки, в которые помещается каждое значение, а не количество элементов в ячейке:

In [3]: eq_width_bin(data,3)                                                    
Out[3]: [1, 2, 1, 0, 0, 0, 1, 1, 2, 2, 2, 1]

Ваши ящики:

In [10]: np.linspace(np.min(data),np.max(data),4)                               
Out[10]: array([ 10.,  50.,  90., 130.])

мы можем определить ячейку для каждого значения с помощью простого целочисленного деления:

In [12]: (data-10)//40                                                          
Out[12]: array([1, 2, 1, 0, 0, 0, 1, 1, 2, 2, 3, 1])

и исправить это 3 с помощью:

In [16]: np.minimum((data-10)//40,2)                                            
Out[16]: array([1, 2, 1, 0, 0, 0, 1, 1, 2, 2, 2, 1])

Но это не отвечает на ваш вопрос о .select .collect .inject .sort_by. Я не знаком с ними (хотя я был фанатом Squeak лет назад и немного баловался с Ruby). Они больше похожи на итераторы, такие как собранные в itertools.

С numpy мы обычно не используем итеративный подход. Скорее мы пытаемся посмотреть на массивы в целом, делая такие вещи, как деление и мин / макс для всего этого.

===

searchsorted также работает для этой проблемы:

In [19]: np.searchsorted(Out[10],data)                                              
Out[19]: array([2, 3, 2, 1, 1, 0, 2, 2, 3, 3, 3, 2])

In [21]: np.maximum(0,np.searchsorted(Out[10],data)-1)                              
Out[21]: array([1, 2, 1, 0, 0, 0, 1, 1, 2, 2, 2, 1])

(возможно) более чистое выражение вашего цикла Python:

def foo(i, edges):
    for j,n in enumerate(edges):
        if i<n:
            return j-1
    return j-1
In [34]: edges = np.linspace(np.min(data),np.max(data),4).tolist()              
In [35]: [foo(i,edges) for i in data]                                           
Out[35]: [1, 2, 1, 0, 0, 0, 1, 1, 2, 2, 2, 1]

Я конвертировал edges в список, потому что его итерация быстрее.

С itertools:

In [55]: [len(list(itertools.takewhile(lambda x: x<i,edges)))-1 for i in data]  
Out[55]: [1, 2, 1, 0, 0, -1, 1, 1, 2, 2, 2, 1]

===

Другой подход

In [45]: edges = np.linspace(np.min(data),np.max(data),4)                       
In [46]: data[:,None]<-edges                                                    
Out[46]: 
array([[False, False, False, False],
       [False, False, False, False],
       [False, False, False, False],
       [False, False, False, False],
       [False, False, False, False],
       [False, False, False, False],
       [False, False, False, False],
       [False, False, False, False],
       [False, False, False, False],
       [False, False, False, False],
       [False, False, False, False],
       [False, False, False, False]])
In [47]: np.argmax(data[:,None]<edges, axis=1)-1                                
Out[47]: array([ 1,  2,  1,  0,  0,  0,  1,  1,  2,  2, -1,  1])

То, что -1 нуждается в очистке (строка, где нет Истины).

редактировать

Списки имеют метод index; с этим мы можем получить выражение, очень похожее на вашу последнюю строку Ruby. Похоже, что понимание списка очень похоже на Ruby collect:

In [88]: [[i<j for i in edges].index(False)-1 for j in data]                    
Out[88]: [1, 2, 1, 0, 0, -1, 1, 1, 2, 2, 2, 1]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...