25 Stimmen

Wie man ein Histogramm aus einem flachen Array in Ruby erstellt

Wie erstelle ich ein Histogramm einer Reihe von Ganzzahlen? Zum Beispiel:

data = [0,1,2,2,2,2,2,3,3,3,3,3,3,4,4,4,4,5,5,6,6,6,7,7,7,7,7,8,9,9,10]

Ich möchte ein Histogramm erstellen, basierend darauf, wie viele Einträge es für 0, 1, 2 usw. gibt. Gibt es einen einfachen Weg, dies in Ruby zu tun?

Die Ausgabe sollte aus zwei Arrays bestehen. Das erste Array sollte die Gruppen (Bins) enthalten, das zweite Array sollte die Anzahl der Vorkommen (Frequenzen) enthalten.

Für das oben angegebene data würde ich die folgende Ausgabe erwarten:

bins         # => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
frequencies  # => [1, 1, 5, 6, 4, 2, 3, 5, 1, 2, 1]

61voto

the Tin Man Punkte 154584

Das Ruby-Array erbt group_by von Enumerable, was das schön macht:

Hash[*data.group_by{ |v| v }.flat_map{ |k, v| [k, v.size] }]

Was zurückgibt:

{
     0 => 1,
     1 => 1,
     2 => 5,
     3 => 6,
     4 => 4,
     5 => 2,
     6 => 3,
     7 => 5,
     8 => 1,
     9 => 2,
    10 => 1
}

Das ist einfach eine saubere Hash-Funktion. Wenn Sie ein Array jedes Bins und Frequenzpaars möchten, können Sie es verkürzen und verwenden:

data = [0,1,2,2,3,3,3,4]
data.group_by{ |v| v }.map{ |k, v| [k, v.size] }
# => [[0, 1], [1, 1], [2, 2], [3, 3], [4, 1]]

Hier ist, was der Code und group_by mit dem kleineren Datensatz machen:

data.group_by{ |v| v }    
# => {0=>[0], 1=>[1], 2=>[2, 2], 3=>[3, 3, 3], 4=>[4]}

data.group_by{ |v| v }.flat_map{ |k, v| [k, v.size] }  
# => [0, 1, 1, 1, 2, 2, 3, 3, 4, 1]

Wie in den Kommentaren von Telmo Costa erwähnt, führte Ruby in v2.7.0 tally ein. Ein schneller Benchmark zeigt, dass tally etwa 3x schneller ist:

require 'fruity'

puts "Ruby v#{RUBY_VERSION}"

data = [0,1,2,2,2,2,2,3,3,3,3,3,3,4,4,4,4,5,5,6,6,6,7,7,7,7,7,8,9,9,10]

data.group_by{ |v| v }.map{ |k, v| [k, v.size] }.to_h
# => {0=>1, 1=>1, 2=>5, 3=>6, 4=>4, 5=>2, 6=>3, 7=>5, 8=>1, 9=>2, 10=>1}
data.group_by { |v| v }.transform_values(&:size)
# => {0=>1, 1=>1, 2=>5, 3=>6, 4=>4, 5=>2, 6=>3, 7=>5, 8=>1, 9=>2, 10=>1}
data.tally 
# => {0=>1, 1=>1, 2=>5, 3=>6, 4=>4, 5=>2, 6=>3, 7=>5, 8=>1, 9=>2, 10=>1}
data.group_by{ |v| v }.keys.sort.map { |key| [key, data.group_by{ |v| v }[key].size] }.to_h
# => {0=>1, 1=>1, 2=>5, 3=>6, 4=>4, 5=>2, 6=>3, 7=>5, 8=>1, 9=>2, 10=>1}

compare do
  gb { data.group_by{ |v| v }.map{ |k, v| [k, v.size] }.to_h }
  rriemann { data.group_by { |v| v }.transform_values(&:size) }
  telmo_costa { data.tally }
  CBK {data.group_by{ |v| v }.keys.sort.map { |key| [key, data.group_by{ |v| v }[key].size] }.to_h }
end

Ergebnis:

# >> Ruby v2.7.0
# >> Jeder Test wird 1024 Mal durchgeführt. Der Test dauert etwa 2 Sekunden.
# >> telmo_costa ist 2x schneller als rriemann ± 0.1
# >> rriemann ist ähnlich schnell wie gb
# >> gb ist 8x schneller als CBK ± 1.0

Also verwenden Sie tally.

10voto

Rahul Jiresal Punkte 1006

Verwenden Sie "Histogramm".

data = [0,1,2,2,2,2,2,3,3,3,3,3,3,4,4,4,4,5,5,6,6,6,7,7,7,7,7,8,9,9,10]
(bins, freqs) = data.histogram 

Dies erstellt ein Array bins, das die Bins des Histogramms enthält, und das Array freqs, das die Häufigkeiten enthält. Das Paket unterstützt auch verschiedene Binning-Verhaltensweisen und Gewichte/Bruchteile.

Hoffentlich hilft das.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X