मैं रूबी सरणी से औसत कैसे बनाऊं?


209

किसी ऐरे से औसत कैसे मिलेगा?

अगर मेरे पास सरणी है:

[0,4,8,2,5,0,2,6]

एवरेजिंग मुझे 3.375 देगा।


11
यदि आप उन संख्याओं के औसत के रूप में 21.75 प्राप्त कर रहे हैं, तो कुछ बहुत ही गलत है ...
ceejayoz

2
dotty, निश्चित नहीं है कि आपको 21.75 कैसे मिला लेकिन डेटा के उस सेट के लिए औसत / औसत 3.375 है और योग 27 है। मुझे यकीन नहीं है कि किस प्रकार का एकत्रीकरण समारोह 21.75 होगा। कृपया दोगुना चेक करें और सुनिश्चित करें कि आप वास्तव में वही हैं जो आप के बाद हैं!
पॉल सासिक

2
मुझे नहीं पता कि मुझे 21.75 कहां से मिला। कैलकुलेटर पर 0 + 48 + 2 + 5 + 0 + 2 + 6 जैसे कुछ दबाया जाना चाहिए था!
दीवाना हो गया

16
चूंकि यह रूबी-ऑन-रेल भी टैग किया गया है, यदि आप ActiveRecord सरणी से औसत हैं, तो सक्रिय रिकॉर्ड की गणना देखने लायक है। व्यक्ति.उपयोग (: आयु, देश: => 'ब्राज़ील') ब्राज़ील के लोगों की औसत आयु देता है। बहुत अच्छा!
काइल हिरोनिमस

जवाबों:


259

इसे इस्तेमाल करे:

arr = [5, 6, 7, 8]
arr.inject{ |sum, el| sum + el }.to_f / arr.size
=> 6.5

ध्यान दें .to_f, जो आप पूर्णांक विभाजन से किसी भी समस्या से बचने के लिए चाहते हैं। आप भी कर सकते हैं:

arr = [5, 6, 7, 8]
arr.inject(0.0) { |sum, el| sum + el } / arr.size
=> 6.5

आप इसे Arrayएक और टिप्पणीकार के सुझाव के रूप में परिभाषित कर सकते हैं , लेकिन आपको पूर्णांक विभाजन से बचने की आवश्यकता है या आपके परिणाम गलत होंगे। इसके अलावा, यह आम तौर पर हर संभव तत्व प्रकार पर लागू नहीं होता है (जाहिर है, एक औसत केवल उन चीजों के लिए समझ में आता है जो औसतन हो सकते हैं)। लेकिन अगर आप उस मार्ग पर जाना चाहते हैं, तो इसका उपयोग करें:

class Array
  def sum
    inject(0.0) { |result, el| result + el }
  end

  def mean 
    sum / size
  end
end

यदि आपने पहले नहीं देखा है inject, तो यह उतना जादुई नहीं है जितना यह दिखाई दे सकता है। यह प्रत्येक तत्व पर पुनरावृत्ति करता है और फिर उस पर एक संचायक मान लागू करता है। फिर संचायक को अगले तत्व को सौंप दिया जाता है। इस मामले में, हमारा संचायक केवल एक पूर्णांक है जो पिछले सभी तत्वों के योग को दर्शाता है।

संपादित करें: टिप्पणीकार डेव रे ने एक अच्छा सुधार प्रस्तावित किया।

संपादित करें: टिप्पणीकार ग्लेन जैकमैन का प्रस्ताव, उपयोग करना arr.inject(:+).to_f, बहुत अच्छा है, लेकिन शायद थोड़ा बहुत चालाक है अगर आपको नहीं पता कि क्या चल रहा है। :+प्रतीक है; जब इसे इंजेक्ट किया जाता है, तो यह संचयकर्ता मूल्य के विरुद्ध प्रत्येक तत्व को प्रतीक द्वारा नामित विधि (इस मामले में, अतिरिक्त संचालन) को लागू करता है।


6
आप to_f और को खत्म कर सकते हैं? इंजेक्शन लगाने के लिए एक प्रारंभिक मूल्य पास करके ऑपरेटर arr.inject(0.0) { |sum,el| sum + el } / arr.size:।
डेव रे

103
या: arr.inject (: +) ._f / arr.size # =>
3.375

5
मुझे नहीं लगता कि यह वारंट ऐरे वर्ग को जोड़ रहा है, क्योंकि यह उन सभी प्रकारों के लिए सामान्य नहीं है, जिनमें एरे शामिल हो सकते हैं।
सारा मेई

8
@ जॉन: यह बिल्कुल प्रतीकात्मक नहीं है # to_proc रूपांतरण - यह injectइंटरफ़ेस का हिस्सा है , जिसका प्रलेखन में उल्लेख किया गया है। to_procऑपरेटर है &
चक

21
यदि आप रेल का उपयोग कर रहे हैं, Array#injectतो यहां ओवरकिल है। बस उपयोग करें #sum। उदाarr.sum.to_f / arr.size
निक

113
a = [0,4,8,2,5,0,2,6]
a.instance_eval { reduce(:+) / size.to_f } #=> 3.375

इसका एक संस्करण जो उपयोग नहीं करता है वह instance_evalहोगा:

a = [0,4,8,2,5,0,2,6]
a.reduce(:+) / a.size.to_f #=> 3.375

4
मुझे नहीं लगता कि यह बहुत चालाक है। मुझे लगता है कि यह समस्या को मुहावरे में हल करता है। यानी, यह कम उपयोग करता है, जो बिल्कुल सही है। प्रोग्रामर्स को यह समझने के लिए प्रोत्साहित किया जाना चाहिए कि क्या सही है, क्यों सही है, और फिर प्रचार करें। औसत, सच जैसे तुच्छ ऑपरेशन के लिए, किसी को "चतुर" होने की आवश्यकता नहीं है। लेकिन यह समझने से कि "कम" एक तुच्छ मामले के लिए है, कोई भी इसे और अधिक जटिल समस्याओं पर लागू करना शुरू कर सकता है। वोट दें।
pduey

3
यहाँ Inst_eval की आवश्यकता क्यों है?
tybro0103

10
instance_evalआपको केवल aएक बार निर्दिष्ट करते हुए कोड चलाने देता है , इसलिए इसे अन्य आदेशों के साथ जंजीर बनाया जा सकता है। यानी random_average = Array.new(10) { rand(10) }.instance_eval { reduce(:+) / size.to_f } बजायrandom = Array.new(10) { rand(10) }; random_average = random.reduce(:+) / random.size
बेंजामिन Manns

2
मुझे नहीं पता, इस तरह से Inst_eval का उपयोग करना बस अजीब लगता है, और इसके साथ बहुत सारे गौचे जुड़े हैं जो इस दृष्टिकोण को एक बुरा विचार बनाते हैं, IMO। (उदाहरण के लिए, यदि आपने वैरिएबल या selfउस ब्लॉक के अंदर चर या विधि का उपयोग करने का प्रयास किया है, तो आप समस्याओं में भाग लेंगे।) instance_evalमेटाप्रोग्रामिंग या डीएसएल के लिए अधिक है।
अजादि ३२

1
@ Ajedi32 मैं सहमत हूं, अपने एप्लिकेशन कोड में इसका उपयोग न करें। हालाँकि यह बहुत अच्छा था कि वह मेरे उत्तर में पेस्ट करने में सक्षम था: (
एनिमेटेडगिव

94

मेरा मानना ​​है कि सबसे सरल उत्तर है

list.reduce(:+).to_f / list.size

1
मुझे इसे खोजने में एक पल लगा - मिश्रण reduceकी एक विधि है जिसका Enumerableउपयोग किया जाता है Array। और इसके नाम के बावजूद, मैं @ShuWu से सहमत हूं ... जब तक आप रेल का उपयोग नहीं कर रहे हैं जो लागू करता है sum
टॉम हैरिसन

मैं यहां समाधान देखता हूं, मुझे पता है कि वे बहुत साफ-सुथरे दिखते हैं, लेकिन मुझे डर है कि अगर मैं भविष्य में अपने कोड को पढ़ता हूं तो वे अस्पष्ट होंगे। स्वच्छ समाधान के लिए धन्यवाद!
atmosx

मेरे सिस्टम पर यह स्वीकृत उत्तर की तुलना में 3x तेज है।
सर्जियो

48

मैं Math.aiture (मूल्यों) के लिए उम्मीद कर रहा था, लेकिन ऐसी कोई किस्मत नहीं।

values = [0,4,8,2,5,0,2,6]
average = values.sum / values.size.to_f

3
मुझे एहसास नहीं था कि # रेल रेल द्वारा जोड़ा गया था! यह बात बताने के लिए धन्यवाद।
डेनी अब्राहम

11
क्रिसमस 2016 (रूबी 2.4) के बाद, ऐरे में एक विधि होगीsum , इसलिए यह 6 साल बाद एक सही उत्तर प्रतीत होता है, नास्त्रेदमस पुरस्कार के योग्य।
२१:०६ पर स्टेन्सलाग २ ste

38

रूबी संस्करण> = 2.4 में एक एन्युमरेबल # योग है पद्धति है।

और फ्लोटिंग पॉइंट एवरेज पाने के लिए, आप Integer # fdiv का उपयोग कर सकते हैं

arr = [0,4,8,2,5,0,2,6]

arr.sum.fdiv(arr.size)
# => 3.375

पुराने संस्करणों के लिए:

arr.reduce(:+).fdiv(arr.size)
# => 3.375

9

शीर्ष समाधान के कुछ बेंचमार्किंग (सबसे कुशल के क्रम में):

बड़े ऐरे:

array = (1..10_000_000).to_a

Benchmark.bm do |bm|
  bm.report { array.instance_eval { reduce(:+) / size.to_f } }
  bm.report { array.sum.fdiv(array.size) }
  bm.report { array.sum / array.size.to_f }
  bm.report { array.reduce(:+).to_f / array.size }
  bm.report { array.reduce(:+).try(:to_f).try(:/, array.size) }
  bm.report { array.inject(0.0) { |sum, el| sum + el }.to_f / array.size }
  bm.report { array.reduce([ 0.0, 0 ]) { |(s, c), e| [ s + e, c + 1 ] }.reduce(:/) }
end


    user     system      total        real
0.480000   0.000000   0.480000   (0.473920)
0.500000   0.000000   0.500000   (0.502158)
0.500000   0.000000   0.500000   (0.508075)
0.510000   0.000000   0.510000   (0.512600)
0.520000   0.000000   0.520000   (0.516096)
0.760000   0.000000   0.760000   (0.767743)
1.530000   0.000000   1.530000   (1.534404)

छोटे अर्रेज़:

array = Array.new(10) { rand(0.5..2.0) }

Benchmark.bm do |bm|
  bm.report { 1_000_000.times { array.reduce(:+).to_f / array.size } }
  bm.report { 1_000_000.times { array.sum / array.size.to_f } }
  bm.report { 1_000_000.times { array.sum.fdiv(array.size) } }
  bm.report { 1_000_000.times { array.inject(0.0) { |sum, el| sum + el }.to_f / array.size } }
  bm.report { 1_000_000.times { array.instance_eval { reduce(:+) / size.to_f } } }
  bm.report { 1_000_000.times { array.reduce(:+).try(:to_f).try(:/, array.size) } }
  bm.report { 1_000_000.times { array.reduce([ 0.0, 0 ]) { |(s, c), e| [ s + e, c + 1 ] }.reduce(:/) } }
end


    user     system      total        real
0.760000   0.000000   0.760000   (0.760353)
0.870000   0.000000   0.870000   (0.876087)
0.900000   0.000000   0.900000   (0.901102)
0.920000   0.000000   0.920000   (0.920888)
0.950000   0.000000   0.950000   (0.952842)
1.690000   0.000000   1.690000   (1.694117)
1.840000   0.010000   1.850000   (1.845623)

आपका बेंचमार्क थोड़ा गलत है। बेंचमार्क / ips वास्तव में इस तरह की तुलना के लिए बेहतर है। इसके अलावा, मैं नकारात्मक और सकारात्मक संख्याओं के साथ-साथ फ्लोट के साथ बेतरतीब ढंग से आबाद एक ऐरे का उपयोग करने का सुझाव दूंगा, ताकि अधिक यथार्थवादी परिणाम मिल सके। आप पाएंगे कि example_eval array.sum.fdiv से धीमा है। तैरने के लिए लगभग 8 गुना। और पूर्णांक के लिए X1.12 के बारे में। साथ ही, अलग-अलग OS अलग परिणाम देंगे। मेरे मैक पर इनमें से कुछ विधियां मेरे लिनक्स
ड्रॉप्लेट की

इसके अलावा योग पद्धति योग की गणना के बजाय पर्वतमाला पर गॉस के सूत्र का उपयोग करती है।
संतोष

4
class Array
  def sum 
    inject( nil ) { |sum,x| sum ? sum+x : x }
  end

  def mean 
    sum.to_f / size.to_f
  end
end

[0,4,8,2,5,0,2,6].mean

2
पूर्णांक विभाजन के कारण यह गलत मान देता है। उदाहरण के लिए, [2,3] .mean के साथ इसे आज़माएं, जो 2.5 के बजाय 2 देता है।
जॉन फेमिनाला

1
खाली सरणी में nil0 के बजाय एक योग क्यों होना चाहिए ?
एंड्रयू ग्रिम

1
क्योंकि आपको [] और [0] में अंतर मिल सकता है। और मुझे लगता है हर कोई है जो एक वास्तविक मतलब to_i का इस्तेमाल करते हैं या एक 0 के साथ ऊपर नहीं के बराबर की जगह ले सकता चाहते हैं
astropanic

4

मुझे प्रतियोगिता में कुछ लाने दो जो शून्य समस्या द्वारा विभाजन को हल करता है:

a = [1,2,3,4,5,6,7,8]
a.reduce(:+).try(:to_f).try(:/,a.size) #==> 4.5

a = []
a.reduce(:+).try(:to_f).try(:/,a.size) #==> nil

हालाँकि, मुझे स्वीकार करना चाहिए कि "कोशिश" एक रेल सहायक है। लेकिन आप इसे आसानी से हल कर सकते हैं:

class Object;def try(*options);self&&send(*options);end;end
class Array;def avg;reduce(:+).try(:to_f).try(:/,size);end;end

BTW: मुझे लगता है कि यह सही है कि एक खाली सूची का औसत शून्य है। कुछ नहीं का औसत कुछ भी नहीं है, 0. इसलिए यह अपेक्षित व्यवहार नहीं है। हालाँकि, यदि आप इसमें परिवर्तन करते हैं:

class Array;def avg;reduce(0.0,:+).try(:/,size);end;end

खाली एरे के लिए परिणाम एक अपवाद नहीं होगा जैसा कि मैंने उम्मीद की थी, लेकिन इसके बजाय NaN देता है ... मैंने रूबी में पहले कभी नहीं देखा है। ;; को फ्लोट वर्ग का एक विशेष व्यवहार लगता है ...

0.0/0 #==> NaN
0.1/0 #==> Infinity
0.0.class #==> Float

4

मुझे स्वीकृत समाधान के बारे में क्या पसंद नहीं है

arr = [5, 6, 7, 8]
arr.inject{ |sum, el| sum + el }.to_f / arr.size
=> 6.5

यह है कि यह वास्तव में शुद्ध रूप से काम नहीं करता है। हमें अंत में arr.size की गणना करने के लिए एक चर गिरफ्तारी की आवश्यकता है।

इस विशुद्ध रूप से कार्यात्मक रूप से हल करने के लिए हमें दो मूल्यों पर नज़र रखने की आवश्यकता है: सभी तत्वों का योग, और तत्वों की संख्या।

[5, 6, 7, 8].inject([0.0,0]) do |r,ele|
    [ r[0]+ele, r[1]+1 ]
end.inject(:/)
=> 6.5   

संतोष ने इस समाधान में सुधार किया: तर्क आर के बजाय एक सरणी होने के कारण, हम विनाशकारी उपयोग कर सकते हैं इसे तुरंत दो चर के अलावा लेने के लिए

[5, 6, 7, 8].inject([0.0,0]) do |(sum, size), ele| 
   [ sum + ele, size + 1 ]
end.inject(:/)

यदि आप यह देखना चाहते हैं कि यह कैसे काम करता है, कुछ पुट जोड़ें:

[5, 6, 7, 8].inject([0.0,0]) do |(sum, size), ele| 
   r2 = [ sum + ele, size + 1 ]
   puts "adding #{ele} gives #{r2}"
   r2
end.inject(:/)

adding 5 gives [5.0, 1]
adding 6 gives [11.0, 2]
adding 7 gives [18.0, 3]
adding 8 gives [26.0, 4]
=> 6.5

हम राशि और गणना को समाहित करने के लिए एक सरणी के बजाय एक संरचना का उपयोग कर सकते हैं, लेकिन फिर हमें पहले संरचना को घोषित करना होगा:

R=Struct.new(:sum, :count)
[5, 6, 7, 8].inject( R.new(0.0, 0) ) do |r,ele|
    r.sum += ele
    r.count += 1
    r
end.inject(:/)

यह पहली बार है जब मैं end.methodरूबी में इस्तेमाल किया गया था, इसके लिए धन्यवाद!
एपिगीन

इंजेक्शन विधि के लिए पारित सरणी छितरी हुई हो सकती है। arr.inject([0.0,0]) { |(sum, size), el| [ sum + el, size + 1 ] }.inject(:/)
संतोष San

@ संतोष: हाँ, यह बहुत अधिक पठनीय है! मैं इसे "फैलाव" नहीं कहूंगा, हालांकि, मैं इसे "विनाशकारी" tony.pitluga.com/2011/08/08/destructuring-with-ruby.html
bjelli

3

सार्वजनिक मनोरंजन के लिए, फिर भी एक और समाधान:

a = 0, 4, 8, 2, 5, 0, 2, 6
a.reduce [ 0.0, 0 ] do |(s, c), e| [ s + e, c + 1 ] end.reduce :/
#=> 3.375

1
अगर यह मतदान में अधिक होता तो मैं इसे नहीं समझता! बहुत अच्छा।
मैट स्टीवेंस

स्पष्ट चालाक से बेहतर है , कोड का यह टुकड़ा स्पष्ट नहीं है।
सेबेस्टियन पाल्मा

2

इस पीसी पर माणिक न करें, लेकिन इस सीमा तक कुछ काम करना चाहिए:

values = [0,4,8,2,5,0,2,6]
total = 0.0
values.each do |val|
 total += val
end

average = total/values.size

2

जोड़ें Array#average

मैं एक ही काम काफी बार कर रहा था इसलिए मुझे लगा कि Arrayएक साधारण के साथ सिर्फ कक्षा का विस्तार करना समझदारी हैaverage विधि के । यह किसी भी संख्या के अलावा काम नहीं करता है जैसे कि इंटेगर या फ्लोट्स या दशमलव जैसी संख्या लेकिन जब आप इसे सही उपयोग करते हैं तो यह आसान है।

मैं रूबी ऑन रेल्स का उपयोग कर रहा हूं इसलिए मैंने इसे रखा है config/initializers/array.rbलेकिन आप इसे कहीं भी रख सकते हैं जो बूट पर शामिल है, आदि।

config/initializers/array.rb

class Array

  # Will only work for an Array of numbers like Integers, Floats or Decimals.
  #
  # Throws various errors when trying to call it on an Array of other types, like Strings.
  # Returns nil for an empty Array.
  #
  def average
    return nil if self.empty?

    self.sum / self.size
  end

end

1
a = [0,4,8,2,5,0,2,6]
sum = 0
a.each { |b| sum += b }
average = sum / a.length

4
पूर्णांक विभाजन के कारण यह गलत मान लौटाएगा। उदाहरण के लिए, यदि a [2, 3] है, तो अपेक्षित परिणाम 2.5 है, लेकिन आप 2. वापस आ जाएंगे।
जॉन फेमिनाला

1
a = [0,4,8,2,5,0,2,6]
a.empty? ? nil : a.reduce(:+)/a.size.to_f
=> 3.375

एकल शून्य, पूर्णांक विभाजन से विभाजित होता है और पढ़ने में आसान होता है। यदि आप खाली सरणी रिटर्न 0 चुनते हैं तो आसानी से संशोधित किया जा सकता है।

मुझे यह वैरिएंट भी पसंद है, लेकिन यह थोड़ा और चिंताजनक है।

a = [0,4,8,2,5,0,2,6]
a.empty? ? nil : [a.reduce(:+), a.size.to_f].reduce(:/)
=> 3.375


1

यह तरीका मददगार हो सकता है।

def avg(arr)
  val = 0.0

  arr.each do |n|
    val += n
  end

  len = arr.length

  val / len 
end

p avg([0,4,8,2,5,0,2,6])

1
यहाँ पर अतिशयोक्ति को रोकने के लिए आपका स्वागत है प्रश्न का मूल पोस्टर 3.375 के रूप में उत्तर चाहता है और आपका समाधान 3. i, e
27/8

आपकी टिप्पणीयों के लिए धन्यवाद। मुझे पता है कि प्रश्न का मूल पोस्टर 3.375 के रूप में उत्तर चाहता है और यह विधि वैसा ही करती है जैसा कि मैंने चर 'var' को एक फ्लोट मान (यानी; 0.0) दिया है। मुनीम मुन्ना मुझे यू से सहमत होना है वास्तव में ऐसा ही एक है।
किशोर बुधाथोकी

0

सरणी को दोहराने के बिना (जैसे एक-लाइनर्स के लिए एकदम सही):

[1, 2, 3, 4].then { |a| a.sum.to_f / a.size }


हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.