Python: как создать гистограмму с одинаковыми по размеру ячейками

У меня есть набор данных, и я хочу сделать из него гистограмму. Мне нужно, чтобы ящики были такими жеразмерЯ имею в виду, что они должны содержать одинаковое количество объектов, а не более общую (numpy.histogram) проблему наличияна равном расстоянии бункера. Естественно, это произойдет за счет ширины ящиков, которая может - и, как правило, будет - отличаться.

Я укажу количество желаемых лотков и набор данных, получив взамен ребра лотков.

Example:
data = numpy.array([1., 1.2, 1.3, 2.0, 2.1, 2.12])
bins_edges = somefunc(data, nbins=3)
print(bins_edges)
>> [1.,1.3,2.1,2.12]

Таким образом, все контейнеры содержат 2 точки, но их ширина (0,3, 0,8, 0,02) различна.

Есть два ограничения: - если группа данных идентична, корзина, содержащая их, может быть больше. - если имеется N данных и запрошено M элементов, будет N / M элементов плюс один, если N% M не равно 0.

Этот фрагмент кода - некоторая бессмыслица, которую я написал, которая хорошо работала для небольших наборов данных. Что если я наберу 10 ** 9 + баллов и захочу ускорить процесс?

  1 import numpy as np
  2 
  3 def def_equbin(in_distr, binsize=None, bin_num=None):
  4 
  5     try:
  6 
  7         distr_size = len(in_distr)
  8 
  9         bin_size = distr_size / bin_num
 10         odd_bin_size = distr_size % bin_num
 11 
 12         args = in_distr.argsort()
 13 
 14         hist = np.zeros((bin_num, bin_size))
 15 
 16         for i in range(bin_num):
 17             hist[i, :] = in_distr[args[i * bin_size: (i + 1) * bin_size]]
 18 
 19         if odd_bin_size == 0:
 20             odd_bin = None
 21             bins_limits = np.arange(bin_num) * bin_size
 22             bins_limits = args[bins_limits]
 23             bins_limits = np.concatenate((in_distr[bins_limits],
 24                                           [in_distr[args[-1]]]))
 25         else:
 26             odd_bin = in_distr[args[bin_num * bin_size:]]
 27             bins_limits = np.arange(bin_num + 1) * bin_size
 28             bins_limits = args[bins_limits]
 29             bins_limits = in_distr[bins_limits]
 30             bins_limits = np.concatenate((bins_limits, [in_distr[args[-1]]]))
 31 
 32         return (hist, odd_bin, bins_limits)
 aganders312 окт. 2012 г., 18:13
Возможно, я не правильно понимаю это, но, похоже, у вас получится очень скучная (например, абсолютно плоская) гистограмма. Вы просто ищете некоторые квантили данных?
 astabada12 окт. 2012 г., 18:15
Привет, ты правильно понял. Поскольку каждое значение является величиной галактики, я смогу посмотреть, как ведут себя другие свойства в каждой отдельной корзине!
 ezod12 окт. 2012 г., 18:26
Это больше похоже на квантили, чем на гистограмму.

Ответы на вопрос(4)

Решение Вопроса

from scipy import stats
bin_edges = stats.mstats.mquantiles(data, [0, 2./6, 4./6, 1])
>> array([1. , 1.24666667, 2.05333333, 2.12])
 jimh25 янв. 2017 г., 02:00
@mrchampe самый модный.
 mrchampe12 окт. 2012 г., 18:27
Разве это не просто замечательно, когда вы выучите новый термин, а потом вдруг кажется, что Google снова начинает работать? Бывает со мной все время.
 astabada12 окт. 2012 г., 18:24
Ах, ах, гениально! Я не знал, что их называли квантилями, поэтому я потратил много времени на поиски в «одинаково разнесенных корзинах» и тому подобное ... Большое спасибо!

лине! Очевидно, что вы никогда не сможете разделить на одинаково заполненные ячейки, если количество выборок не делится точно на количество ячеек.

import math
import numpy as np
data = np.array([2,3,5,6,8,5,5,6,3,2,3,7,8,9,8,6,6,8,9,9,0,7,5,3,3,4,5,6,7])
data_sorted = np.sort(data)
nbins = 3
step = math.ceil(len(data_sorted)//nbins+1)
binned_data = []
for i in range(0,len(data_sorted),step):
    binned_data.append(data_sorted[i:i+step])

Я столкнулся с той же проблемой, что и @astabada, и хотел создать бункеры, каждый из которых содержал бы одинаковое количество образцов. Применяя решение, предложенное @ aganders3, я обнаружил, что оно не очень хорошо работает для искаженных дистрибутивов. В случае искаженных данных (например, что-то с большим количеством нулей),stats.mstats.mquantiles для заранее определенного количества квантилей не будет гарантировано равное количество выборок в каждом бине. Вы получите края бина, которые выглядят так:

[0. 0. 4. 9.]

В этом случае первый контейнер будет пустым.

Чтобы справиться с искаженными случаями, я создал функцию, которая вызываетstats.mstats.mquantiles и затем динамически изменяет количество бинов, если выборки не равны в пределах определенного допуска (30% наименьшего размера выборки в примере кода). Если выборки не равны между ячейками, код сокращает число равноотстоящих квантилей на 1 и вызываетstats.mstats.mquantiles снова, пока размеры выборки не будут равны, или существует только один контейнер.

Я жестко закодировал допуск в примере, но при желании его можно изменить на аргумент ключевого слова.

Я также предпочитаю указывать количество одинаково распределенных квантилей в качестве аргумента моей функции, вместо того, чтобы задавать определяемые пользователем квантили дляstats.mstats.mquantiles чтобы уменьшить случайные ошибки (т.е. что-то вроде[0., 0.25, 0.7, 1.]).

Вот код:

import numpy as np 
from scipy import stats

def equibins(dat, binnum, **kwargs):
    numin = binnum
    while numin>1.:
        qtls = np.linspace(0.,1.0,num=numin,endpoint=False)
        ebins =stats.mstats.mquantiles(dat,qtls,alphap=kwargs['alpha'],betap=kwargs['beta'])
        allhist, allbin   = np.histogram(dat, bins = ebins)
        if (np.unique(ebins).shape!=ebins.shape or tolerence(allhist,0.3)==False) and numin>2:
            numin= numin-1
            del qtls, ebins
        else:
            numin=0
    return ebins

def tolerence(narray, percent):
    if percent>1.0:
        per = percent/100.
    else:
        per = percent
    lev_tol  = per*narray.min()
    tolerate = np.all(narray[1:]-narray[0]<lev_tol)
    return tolerate

Я хотел бы отметить также существованиеpandas.qcut, что делает равномерное биннинг довольно эффективным способом. В вашем случае это будет работать как

data = np.array([1., 1.2, 1.3, 2.0, 2.1, 2.12])
# parameter q specifies the number of bins
qc = pd.qcut(data, q=3, precision=1)

# bin definition
bins  = qc.categories
print(bins)
>> Index(['[1, 1.3]', '(1.3, 2.03]', '(2.03, 2.1]'], dtype='object')

# bin corresponding to each point in data
codes = qc.codes
print(codes)
>> array([0, 0, 1, 1, 2, 2], dtype=int8)

Ваш ответ на вопрос