присвоение баллов бинам

Каков хороший способ сгруппировать числовые значения в определенный диапазон? Например, предположим, что у меня есть список значений, и я хочу разбить их на N ячеек по их диапазону. Прямо сейчас я делаю что-то вроде этого:

from scipy import *
num_bins = 3 # number of bins to use
values = # some array of integers...
min_val = min(values) - 1
max_val = max(values) + 1
my_bins = linspace(min_val, max_val, num_bins)
# assign point to my bins
for v in values:
  best_bin = min_index(abs(my_bins - v))

где min_index возвращает индекс минимального значения. Идея состоит в том, что вы можете найти ячейку, в которую попадает точка, увидев, с какой ячейкой она имеет наименьшую разницу.

Но я думаю, что это имеет странные крайние случаи. То, что я ищу, - это хорошее представление бинов, в идеале полузакрытых и полуоткрытых (чтобы не было возможности присвоить одну точку двум бинам), т.е.

bin1 = [x1, x2)
bin2 = [x2, x3)
bin3 = [x3, x4)
etc...

как это сделать на Python, используя numpy/scipy? Меня здесь интересуют только целочисленные значения.

Большое спасибо за вашу помощь.


person Community    schedule 27.01.2010    source источник
comment
в качестве примечания: я более чем готов использовать matplotlib в дополнение к scipy/numpy, если он имеет эту функциональность. Я предполагаю, что такие функции, как «hist», должны делать что-то подобное, за исключением того, что здесь я не ищу никаких графиков.   -  person    schedule 27.01.2010


Ответы (2)


numpy.histogram() делает именно то, что вы хотите.

Сигнатура функции:

numpy.histogram(a, bins=10, range=None, normed=False, weights=None, new=None)

Нас больше всего интересуют a и bins. a — это входные данные, которые необходимо сгруппировать. bins может быть числом бинов (ваше num_bins) или последовательностью скаляров, обозначающих ребра бинов (полуоткрытые).

import numpy
values = numpy.arange(10, dtype=int)
bins = numpy.arange(-1, 11)
freq, bins = numpy.histogram(values, bins)
# freq is now [0 1 1 1 1 1 1 1 1 1 1]
# bins is unchanged

Чтобы процитировать документацию:

Все ячейки, кроме последней (самой правой), полуоткрыты. Другими словами, если bins:

[1, 2, 3, 4]

тогда первый бин равен [1, 2) (включая 1, но исключая 2), а второй [2, 3). Однако последним бином является [3, 4], который включает 4.

Изменить: вы хотите знать индекс каждого элемента в своих корзинах. Для этого вы можете использовать numpy.digitize(). Если ваши контейнеры будут цельными, вы также можете использовать numpy.bincount().

>>> values = numpy.random.randint(0, 20, 10)
>>> values
array([17, 14,  9,  7,  6,  9, 19,  4,  2, 19])
>>> bins = numpy.linspace(-1, 21, 23)
>>> bins
array([ -1.,   0.,   1.,   2.,   3.,   4.,   5.,   6.,   7.,   8.,   9.,
        10.,  11.,  12.,  13.,  14.,  15.,  16.,  17.,  18.,  19.,  20.,
        21.])
>>> pos = numpy.digitize(values, bins)
>>> pos
array([19, 16, 11,  9,  8, 11, 21,  6,  4, 21])

Поскольку интервал открыт на верхнем пределе, индексы правильные:

>>> (bins[pos-1] == values).all()
True
>>> import sys
>>> for n in range(len(values)):
...     sys.stdout.write("%g <= %g < %g\n"
...             %(bins[pos[n]-1], values[n], bins[pos[n]]))
17 <= 17 < 18
14 <= 14 < 15
9 <= 9 < 10
7 <= 7 < 8
6 <= 6 < 7
9 <= 9 < 10
19 <= 19 < 20
4 <= 4 < 5
2 <= 2 < 3
19 <= 19 < 20
person Alok Singhal    schedule 27.01.2010
comment
спасибо за ваш ответ, но я думаю, что гистограмма все еще отличается от того, что я хочу. Меня не интересует частота какого-либо из бинов, я просто хочу знать, в какой бин попадает каждая точка. Кажется, что гистограмма не возвращает эту информацию, верно? - person ; 27.01.2010
comment
О, тогда вам стоит взглянуть на numpy.digitize(). - person Alok Singhal; 27.01.2010

Это довольно просто в numpy с использованием широковещательной передачи — мой пример ниже состоит из четырех строк кода (не считая первых двух строк для создания ячеек и точек данных, которые, конечно, обычно предоставляются).

import numpy as NP
# just creating 5 bins at random, each bin expressed as (x, y, z) although, this code
# is not limited by bin number or bin dimension
bins = NP.random.random_integers(10, 99, 15).reshape(5, 3) 
# creating 30 random data points
data = NP.random.random_integers(10, 99, 90).reshape(30, 3)
# for each data point i want the nearest bin, but before i can generate a distance
# matrix, i need to 'conform' the array dimensions
# 'broadcasting' is an excellent and concise way to do this
bins = bins[:, NP.newaxis, :]
data2 = data[NP.newaxis, :, :]
# now i can calculate the distance matrix
dist_matrix = NP.sqrt(NP.sum((data - bins)**2, axis=-1)) 
bin_assignments = NP.argmin(dist_matrix, axis=0)

«bin_assignments» — это массив индексов из 1d, состоящий из целых значений от 0 до 4, соответствующих пяти бинам — назначениям бинов для каждой из 30 исходных точек в матрице «данных» выше.

person doug    schedule 31.01.2010
comment
Я не очень хорошо понимаю этот ответ, не могли бы вы объяснить его лучше? - person G M; 23.11.2016