Python эквивалент для улья NumericHistogram - PullRequest
0 голосов
/ 02 сентября 2018

У меня есть код на Python (точнее, на pypy-совместимый). Я должен использовать гистограммы в нем, которые должны быть очень похожи, если не точно такие же, как эта реализация гистограммы Java в проекте Hive. У Numpy есть гистограмма , реализация . Кто-нибудь знает, эквивалентны ли эти два в использовании или могут быть сделаны путем выбора соответствующих значений параметров? Я могу найти это, читая коды, хотя проверяю здесь, если кто-то уже знает это.

1 Ответ

0 голосов
/ 02 сентября 2018

Длинный ответ, TLDR жирным шрифтом

Вы можете просто скомпилировать и запустить оба, вместо того, чтобы пытаться теоретизировать и отгадывать, что делает код. Если есть сомнения, просто запустите его. Немного потрудившись, вы можете получить NumericHistogram.java, который вы связали для компиляции без maven и просто с помощью вызова CLI javac (просто удалите ссылки на hive пакеты и связанные методы).

Я просто протестировал оба на массиве [0,1,...,98,99].

Java-версия (Java 10.0.2):

РЕДАКТИРОВАТЬ: Получил отзыв (по электронной почте), чтобы включить код Java. Вот оно (удалены строки документации и некоторые комментарии, а не все общедоступные методы):

/*                                                                                                                                                                                                          
 * Licensed to the Apache Software Foundation (ASF) under one                                                                                                                                               
 * or more contributor license agreements.  See the NOTICE file                                                                                                                                             
 * distributed with this work for additional information                                                                                                                                                    
 * regarding copyright ownership.  The ASF licenses this file                                                                                                                                               
 * to you under the Apache License, Version 2.0 (the                                                                                                                                                        
 * "License"); you may not use this file except in compliance                                                                                                                                               
 * with the License.  You may obtain a copy of the License at                                                                                                                                               
 *                                                                                                                                                                                                          
 *     http://www.apache.org/licenses/LICENSE-2.0                                                                                                                                                           
 *                                                                                                                                                                                                          
 * Unless required by applicable law or agreed to in writing, software                                                                                                                                      
 * distributed under the License is distributed on an "AS IS" BASIS,                                                                                                                                        
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.                                                                                                                                 
 * See the License for the specific language governing permissions and                                                                                                                                      
 * limitations under the License.                                                                                                                                                                           
 */

import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
import java.util.Random;

public class NumericHistogram {
    static class Coord implements Comparable {
        double x;
        double y;

        public int compareTo(Object other) {
            return Double.compare(x, ((Coord) other).x);
        }
    };

    // Class variables                                                                                                                                                                                      
    private int nbins;
    private int nusedbins;
    private ArrayList<Coord> bins;
    private Random prng;

    public NumericHistogram() {
        nbins = 0;
        nusedbins = 0;
        bins = null;

        // init the RNG for breaking ties in histogram merging. A fixed seed is specified here                                                                                                              
        // to aid testing, but can be eliminated to use a time-based seed (which would                                                                                                                      
        // make the algorithm non-deterministic).                                                                                                                                                           
        prng = new Random(31183);
    }

    public void reset() {
        bins = null;
        nbins = nusedbins = 0;
    }

    public int getUsedBins() {
        return nusedbins;
    }

    public boolean isReady() {
        return nbins != 0;
    }

    public Coord getBin(int b) {
        return bins.get(b);
    }

    public void allocate(int num_bins) {
        nbins = num_bins;
        bins = new ArrayList<Coord>();
        nusedbins = 0;
    }

    public void add(double v) {
        int bin = 0;
        for(int l=0, r=nusedbins; l < r; ) {
            bin = (l+r)/2;
            if (bins.get(bin).x > v) {
                r = bin;
            } else {
                if (bins.get(bin).x < v) {
                    l = ++bin;
                } else {
                    break; // break loop on equal comparator                                                                                                                                                
                }
            }
        }
        if (bin < nusedbins && bins.get(bin).x == v) {
            bins.get(bin).y++;
        } else {
            Coord newBin = new Coord();
            newBin.x = v;
            newBin.y = 1;
            bins.add(bin, newBin);

            // Trim the bins down to the correct number of bins.                                                                                                                                            
            if (++nusedbins > nbins) {
                trim();
            }
        }

    }

    private void trim() {
        while(nusedbins > nbins) {
            // Find the closest pair of bins in terms of x coordinates. Break ties randomly.                                                                                                                
            double smallestdiff = bins.get(1).x - bins.get(0).x;
            int smallestdiffloc = 0, smallestdiffcount = 1;
            for(int i = 1; i < nusedbins-1; i++) {
                double diff = bins.get(i+1).x - bins.get(i).x;
                if(diff < smallestdiff)  {
                    smallestdiff = diff;
                    smallestdiffloc = i;
                    smallestdiffcount = 1;
                } else {
                    if(diff == smallestdiff && prng.nextDouble() <= (1.0/++smallestdiffcount) ) {
                        smallestdiffloc = i;
                    }
                }
            }

            double d = bins.get(smallestdiffloc).y + bins.get(smallestdiffloc+1).y;
            Coord smallestdiffbin = bins.get(smallestdiffloc);
            smallestdiffbin.x *= smallestdiffbin.y / d;
            smallestdiffbin.x += bins.get(smallestdiffloc+1).x / d * bins.get(smallestdiffloc+1).y;
            smallestdiffbin.y = d;
            // Shift the remaining bins left one position                                                                                                                                                   
            bins.remove(smallestdiffloc+1);
            nusedbins--;
        }
    }

    public int getNumBins() {
        return bins == null ? 0 : bins.size();
    }
}

Я вставил это главное в класс NumericHistogram (см. Выше):

public static void main(String[] args) {
    NumericHistogram hist = new NumericHistogram();
    hist.allocate(10);
    for (int i = 0; i < 100; i++) {
        hist.add(i);
    }

    ArrayList<Coord> bins = new ArrayList<Coord>();
    for (int i = 0; i < 10; i++) {
        bins.add(hist.getBin(i));
    }

    int index = 0;
    for (Coord bin : bins) {
        System.out.println(index + "th bin x: " + bin.x);
        System.out.println(index + "th bin y: " + bin.y);
        index++;
    }
}

Этот вывод:

Matthews-MacBook-Pro:stackoverflow matt$ java NumericHistogram
0th bin x: 2.0
0th bin y: 5.0
1th bin x: 9.5
1th bin y: 10.0
2th bin x: 21.5
2th bin y: 14.0
3th bin x: 33.0
3th bin y: 9.0
4th bin x: 44.0
4th bin y: 13.0
5th bin x: 55.0
5th bin y: 9.0
6th bin x: 64.5
6th bin y: 10.0
7th bin x: 75.5
7th bin y: 12.0
8th bin x: 88.0
8th bin y: 13.0
9th bin x: 97.0
9th bin y: 5.0

Python версия:

Версия numpy была немного другой. Вот сценарий:

import numpy as np

hist = np.histogram(np.arange(100)) # 10 is the default for num bins
print(hist)

Этот вывод:

(array([10, 10, 10, 10, 10, 10, 10, 10, 10, 10]), array([ 0. ,  9.9, 19.8, 29.7, 39.6, 49.5, 59.4, 69.3, 79.2, 89.1, 99. ]))

Чтобы сравнить с java-версией, нам действительно нужно сжать их, что вы можете сделать так:

y_values, x_values = hist
i = 0
for x_val, y_val in zip(x_values, y_values):
    print(str(i) + "th bin x: " + str(x_val))
    print(str(i) + "th bin y: " + str(y_val))
    i += 1

и вывод:

0th bin x: 0.0
0th bin y: 10
1th bin x: 9.9
1th bin y: 10
2th bin x: 19.8
2th bin y: 10
3th bin x: 29.700000000000003
3th bin y: 10
4th bin x: 39.6
4th bin y: 10
5th bin x: 49.5
5th bin y: 10
6th bin x: 59.400000000000006
6th bin y: 10
7th bin x: 69.3
7th bin y: 10
8th bin x: 79.2
8th bin y: 10
9th bin x: 89.10000000000001
9th bin y: 10

Сводка и приведение их в соответствие:

Они эквивалентны? Нет, они не эквивалентны. Они используют разные схемы разбиения для присвоения значений корзинам. Версия Java помещает элемент в «ближайший» контейнер с использованием правил округления, тогда как numpy выполняет группировку диапазонов (т. Е. 0-9.9, 9.9-19.8, ..., 89.1-100). Кроме того, сгенерированные по умолчанию корзины также не одинаковы, что имеет смысл, поскольку схемы очень разные.

Могу ли я заставить их совпадать? Да, но вам нужно выяснить, как генерировать случайные числа точно , как Java в области Python, если вы хотите общую реализацию. . Я попытался скопировать реализацию и заставил ее работать, но я не могу получить случайные числа одинаковыми. Даже если вы используете numpy вместо копирования логики в Python, вам все равно нужно будет генерировать случайные числа Java, чтобы они соответствовали в общем случае. Этот проект показался актуальным, но это только Java 6 RNG, а не более поздние версии Java: https://github.com/MostAwesomeDude/java-random. С риском сделать этот гигантский ответ еще дольше, вот код Python для копирования реализации Java:

""" Just a mindless copy for easy verification. Not good style or performant. """
import random
class Coord:
    def __init__(self, x, y):
        self.x = float(x)
        self.y = float(y)

    def __repr__(self): # debug
        return "Coord(" + str(self.x) + ", " + str(self.y) + ")"

    def __str__(self): # debug
        return "Coord(" + str(self.x) + ", " + str(self.y) + ")"

class Random:
    """ This class needs fixin. You'll have to do some work here to make it match your version of Java. """
    def __init__(self, seed):
        random.seed(seed)

    def nextDouble(self):
        return random.uniform(0, 1)

class NumericHistogram:
    def __init__(self):
        self.nbins = 0
        self.nusedbins = 0
        self.bins = None

        self.prng = Random(31183) # This should behave the same as Java's RNG for your Java version.                                                                                                        

    def allocate(self, num_bins):
        self.nbins = num_bins
        self.bins = []
        self.nusedbins = 0

    def add(self, v):
        bin = 0

        l = 0
        r = self.nusedbins
        while(l < r):
            bin = (l+r)//2
            if self.bins[bin].x > v:
                r = bin
            else:
                if self.bins[bin].x < v:
                    l = bin + 1; bin += 1
                else:
                    break

        if bin < self.nusedbins and self.bins[bin].x == v:
            self.bins[bin].y += 1
        else:
            newBin = Coord(x=v, y=1)
            if bin == len(self.bins):
                self.bins.append(newBin)
            else:
                self.bins[bin] == newBin

            self.nusedbins += 1
            if (self.nusedbins > self.nbins):
                self.trim()
    def trim(self):
        while self.nusedbins > self.nbins:
            smallestdiff = self.bins[1].x - self.bins[0].x
            smallestdiffloc = 0
            smallestdiffcount = 1
            for i in range(1, self.nusedbins-1):
                diff = self.bins[i+1].x - self.bins[i].x
                if diff < smallestdiff:
                    smallestdiff = diff
                    smallestdiffloc = i
                    smallestdiffcount = 1
                else:
                    smallestdiffcount += 1
                    if diff == smallestdiff and self.prng.nextDouble() <= (1.0/smallestdiffcount):
                        smallestdiffloc = i

            d = self.bins[smallestdiffloc].y + self.bins[smallestdiffloc+1].y
            smallestdiffbin = self.bins[smallestdiffloc]
            smallestdiffbin.x *= smallestdiffbin.y / d
            smallestdiffbin.x += self.bins[smallestdiffloc+1].x / d * self.bins[smallestdiffloc+1].y
            smallestdiffbin.y = d
            self.bins.pop(smallestdiffloc+1)
            self.nusedbins -= 1

И "главное":

hist = NumericHistogram()
hist.allocate(10)
for i in range(100):
    hist.add(i)

for ind, bin in enumerate(hist.bins):
    print(str(ind) + "th bin x: " + str(bin.x))
    print(str(ind) + "th bin y: " + str(bin.y))

это выводит:

0th bin x: 4.0
0th bin y: 9.0
1th bin x: 13.0
1th bin y: 9.0
2th bin x: 24.0
2th bin y: 13.0
3th bin x: 35.0
3th bin y: 9.0
4th bin x: 46.5
4th bin y: 14.0
5th bin x: 57.5
5th bin y: 8.0
6th bin x: 66.0
6th bin y: 9.0
7th bin x: 76.49999999999999
7th bin y: 12.0
8th bin x: 88.99999999999999
8th bin y: 13.0
9th bin x: 97.5
9th bin y: 4.0

Так что вроде как близко, но без бананов. Разница заключается в RNG (насколько я могу судить). Я не очень много знаю о Java Random или RNG в целом: так что, возможно, было бы лучше просто опубликовать другой вопрос здесь на SO о том, как генерировать случайные числа точно как Java [insert-your-java-version-here].

HTH и поможет вам начать в правильном направлении.

...