Skip to content

Instantly share code, notes, and snippets.

@tenderlove
Last active May 22, 2023 12:21
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tenderlove/fe2715419ee8d2df17d9b09e4864f1f0 to your computer and use it in GitHub Desktop.
Save tenderlove/fe2715419ee8d2df17d9b09e4864f1f0 to your computer and use it in GitHub Desktop.
# An example of calculating least-squares linear regression fit in Ruby
#
# This is free and unencumbered software released into the public domain.
#
# Anyone is free to copy, modify, publish, use, compile, sell, or
# distribute this software, either in source code form or as a compiled
# binary, for any purpose, commercial or non-commercial, and by any
# means.
#
# In jurisdictions that recognize copyright laws, the author or authors
# of this software dedicate any and all copyright interest in the
# software to the public domain. We make this dedication for the benefit
# of the public at large and to the detriment of our heirs and
# successors. We intend this dedication to be an overt act of
# relinquishment in perpetuity of all present and future rights to this
# software under copyright law.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#
# For more information, please refer to <https://unlicense.org>
class LinearFit < Struct.new :m, :b, :r
def inspect
"y = #{m.round(2)} x + #{b.round(2)}\n" +
"r = #{r.round(2)}"
end
###
# Given a list of points, perform a linear regression. +points+ is a list of
# XY coordinate tuples. For example:
#
# fit = LinearFit.from_points([[1, 2], [2, 4]])
#
# This function returns an instance of a `LinearFit`
# object that contains the values `m` and `b` from the equation `y = mx + b`
# as well as the correlation `r` value
def self.from_points points
# Calculate Mean
x_tmp = 0
y_tmp = 0
points.each { |x, y| x_tmp += x; y_tmp += y }
x_mean = x_tmp / points.length.to_f
y_mean = y_tmp / points.length.to_f
# Calculate Sample Standard Deviation
x_tmp = 0
y_tmp = 0
points.each { |x, y|
x_tmp += (x - x_mean) ** 2
y_tmp += (y - y_mean) ** 2
}
s_x = Math.sqrt(x_tmp / (points.length - 1))
s_y = Math.sqrt(y_tmp / (points.length - 1))
# Calculate Correlation
r = points.inject(0) { |memo, (x, y)|
memo + (((x - x_mean) / s_x) * ((y - y_mean) / s_y))
} / (points.length - 1)
# Calculate m and b
m = r * (s_y / s_x)
b = y_mean - (x_mean * m)
new m, b, r
end
end
if __FILE__ == $0
## Demo
# Make some points with a little noise
points = 100.times.map { |x| [x + 1, x + rand] }
p LinearFit.from_points points
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment