Skip to content

Commit 6208623

Browse files
authored
Merge pull request #1 from kortirso/feature/linear_regression
Feature/linear regression
2 parents 7c3e090 + 346c1ef commit 6208623

10 files changed

Lines changed: 371 additions & 48 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
77
## [Unreleased]
88
### Added
99
- CHANGELOG.md file
10+
- Add simple Linear Regression predictor
1011

1112
## [0.1.1] - 2018-11-19
1213
### Added

README.md

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22

33
Elixir package for machine learning
44

5-
Available algorithms:
5+
Available algorithms for prediction:
6+
7+
- Linear Regression
8+
9+
Available algorithms for classification:
610

711
- K-Nearest Neighbours
812
- Gaussian Naive Bayes
@@ -20,21 +24,50 @@ def deps do
2024
end
2125
```
2226

27+
### Linear Regression
28+
29+
Initialize predictor with data:
30+
31+
```elixir
32+
alias LearnKit.Regression.Linear
33+
predictor = Linear.new([1, 2, 3, 4], [3, 6, 10, 15])
34+
```
35+
36+
Fit data set:
37+
38+
```elixir
39+
predictor = predictor |> Linear.fit
40+
```
41+
42+
Predict using the linear model:
43+
44+
```elixir
45+
predictor |> Linear.predict([4, 8, 13])
46+
```
47+
samples - array of variables, required
48+
49+
Returns the coefficient of determination R^2 of the prediction:
50+
51+
```elixir
52+
predictor |> Linear.score
53+
```
54+
2355
### K-Nearest Neighbours classification
2456

2557
Initialize classificator with data set consists from labels and features:
2658

2759
```elixir
28-
classificator = LearnKit.Knn.new
29-
|> LearnKit.Knn.add_train_data({:a1, [-1, -1]})
30-
|> LearnKit.Knn.add_train_data({:a1, [-2, -1]})
31-
|> LearnKit.Knn.add_train_data({:a2, [1, 1]})
60+
alias LearnKit.Knn
61+
classificator = Knn.new
62+
|> Knn.add_train_data({:a1, [-1, -1]})
63+
|> Knn.add_train_data({:a1, [-2, -1]})
64+
|> Knn.add_train_data({:a2, [1, 1]})
3265
```
3366

3467
Predict label for new feature:
3568

3669
```elixir
37-
LearnKit.Knn.classify(classificator, [feature: [-1, -2], k: 3, weight: "distance"])
70+
Knn.classify(classificator, [feature: [-1, -2], k: 3, weight: "distance"])
3871
```
3972
feature - new feature for prediction, required
4073
k - number of nearest neighbors, optional, default - 3
@@ -46,36 +79,37 @@ Predict label for new feature:
4679
Initialize classificator with data set consists from labels and features:
4780

4881
```elixir
49-
classificator = LearnKit.NaiveBayes.Gaussian.new
50-
|> LearnKit.NaiveBayes.Gaussian.add_train_data({:a1, [-1, -1]})
51-
|> LearnKit.NaiveBayes.Gaussian.add_train_data({:a1, [-2, -1]})
52-
|> LearnKit.NaiveBayes.Gaussian.add_train_data({:a2, [1, 1]})
82+
alias LearnKit.NaiveBayes.Gaussian
83+
classificator = Gaussian.new
84+
|> Gaussian.add_train_data({:a1, [-1, -1]})
85+
|> Gaussian.add_train_data({:a1, [-2, -1]})
86+
|> Gaussian.add_train_data({:a2, [1, 1]})
5387
```
5488

55-
Fit data set
89+
Fit data set:
5690

5791
```elixir
58-
classificator = classificator |> LearnKit.NaiveBayes.Gaussian.fit
92+
classificator = classificator |> Gaussian.fit
5993
```
6094

61-
Return probability estimates for the feature
95+
Return probability estimates for the feature:
6296

6397
```elixir
64-
classificator = classificator |> LearnKit.NaiveBayes.Gaussian.predict_proba([1, 2])
98+
classificator |> Gaussian.predict_proba([1, 2])
6599
```
66100
feature - new feature for prediction, required
67101

68-
Return exact prediction for the feature
102+
Return exact prediction for the feature:
69103

70104
```elixir
71-
classificator = classificator |> LearnKit.NaiveBayes.Gaussian.predict([1, 2])
105+
classificator |> Gaussian.predict([1, 2])
72106
```
73107
feature - new feature for prediction, required
74108

75-
Returns the mean accuracy on the given test data and labels
109+
Returns the mean accuracy on the given test data and labels:
76110

77111
```elixir
78-
classificator = classificator |> LearnKit.NaiveBayes.Gaussian.score
112+
classificator |> Gaussian.score
79113
```
80114

81115
## Contributing

lib/learn_kit/math.ex

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,7 @@ defmodule LearnKit.Math do
6565

6666
def variance(list, list_mean) when is_list(list) do
6767
list
68-
|> Enum.map(fn x ->
69-
list_mean - x
70-
|> :math.pow(2)
71-
end)
68+
|> Enum.map(fn x -> :math.pow(list_mean - x, 2) end)
7269
|> mean
7370
end
7471

@@ -132,4 +129,69 @@ defmodule LearnKit.Math do
132129
others = Enum.map(rows, fn(x) -> tl(x) end)
133130
[firsts | swap_rows_cols(others)]
134131
end
132+
133+
@doc """
134+
Division for 2 elements
135+
136+
## Examples
137+
138+
iex> LearnKit.Math.division(10, 2)
139+
5.0
140+
141+
"""
142+
@spec division(number, number) :: number
143+
144+
def division(x, y) when y != 0 do
145+
x / y
146+
end
147+
148+
@doc """
149+
Calculate the covariance of two lists
150+
151+
## Examples
152+
153+
iex> LearnKit.Math.covariance([1, 2, 3], [14, 17, 25])
154+
5.5
155+
156+
"""
157+
@spec covariance(list, list) :: number
158+
159+
def covariance(x, y) when length(x) == length(y) do
160+
mean_x = mean(x)
161+
mean_y = mean(y)
162+
size = length(x)
163+
164+
Enum.zip(x, y)
165+
|> Enum.map(fn {xi, yi} -> (xi - mean_x) * (yi - mean_y) end)
166+
|> Enum.sum
167+
|> division(size - 1)
168+
end
169+
170+
@doc """
171+
Correlation of two lists
172+
173+
## Examples
174+
175+
iex> LearnKit.Math.correlation([1, 2, 3], [14, 17, 25])
176+
0.9672471299049061
177+
178+
"""
179+
@spec correlation(list, list) :: number
180+
181+
def correlation(x, y) when length(x) == length(y) do
182+
mean_x = mean(x)
183+
mean_y = mean(y)
184+
185+
divider = Enum.zip(x, y)
186+
|> Enum.map(fn {xi, yi} -> (xi - mean_x) * (yi - mean_y) end)
187+
|> Enum.sum
188+
denom_x = x
189+
|> Enum.map(fn xi -> :math.pow(xi - mean_x, 2) end)
190+
|> Enum.sum
191+
denom_y = y
192+
|> Enum.map(fn yi -> :math.pow(yi - mean_y, 2) end)
193+
|> Enum.sum
194+
195+
divider / :math.sqrt(denom_x * denom_y)
196+
end
135197
end

lib/learn_kit/regression/linear.ex

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
defmodule LearnKit.Regression.Linear do
2+
@moduledoc """
3+
Module for Linear Regression algorithm
4+
"""
5+
6+
defstruct factors: [], results: [], coefficients: []
7+
8+
alias LearnKit.Regression.Linear
9+
10+
use Linear.Fit
11+
use Linear.Predict
12+
13+
@type factors :: [number]
14+
@type results :: [number]
15+
@type coefficients :: [number]
16+
17+
@doc """
18+
Creates predictor with empty data_set
19+
20+
## Examples
21+
22+
iex> predictor = LearnKit.Regression.Linear.new
23+
%LearnKit.Regression.Linear{factors: [], results: [], coefficients: []}
24+
25+
"""
26+
@spec new() :: %LearnKit.Regression.Linear{factors: [], results: [], coefficients: []}
27+
28+
def new do
29+
Linear.new([], [])
30+
end
31+
32+
@doc """
33+
Creates predictor with data_set
34+
35+
## Parameters
36+
37+
- factors: Array of predictor variables
38+
- results: Array of criterion variables
39+
40+
## Examples
41+
42+
iex> predictor = LearnKit.Regression.Linear.new([1, 2, 3, 4], [3, 6, 10, 15])
43+
%LearnKit.Regression.Linear{factors: [1, 2, 3, 4], results: [3, 6, 10, 15], coefficients: []}
44+
45+
"""
46+
@spec new(factors, results) :: %LearnKit.Regression.Linear{factors: factors, results: results, coefficients: []}
47+
48+
def new(factors, results) do
49+
%Linear{factors: factors, results: results}
50+
end
51+
52+
@doc """
53+
Fit train data
54+
55+
## Parameters
56+
57+
- predictor: %LearnKit.Regression.Linear{}
58+
59+
## Examples
60+
61+
iex> predictor = predictor |> LearnKit.Regression.Linear.fit
62+
%LearnKit.Regression.Linear{
63+
coefficients: [-1.5, 4.0],
64+
factors: [1, 2, 3, 4],
65+
results: [3, 6, 10, 15]
66+
}
67+
68+
"""
69+
@spec fit(%LearnKit.Regression.Linear{factors: factors, results: results}) :: %LearnKit.Regression.Linear{factors: factors, results: results, coefficients: coefficients}
70+
71+
def fit(%Linear{factors: factors, results: results}) do
72+
%Linear{factors: factors, results: results, coefficients: fit_data(factors, results)}
73+
end
74+
75+
@doc """
76+
Predict using the linear model
77+
78+
## Parameters
79+
80+
- predictor: %LearnKit.Regression.Linear{}
81+
- samples: Array of variables
82+
83+
## Examples
84+
85+
iex> predictor |> LearnKit.Regression.Linear.predict([4, 8, 13])
86+
{:ok, [14.5, 30.5, 50.5]}
87+
88+
"""
89+
@spec predict(%LearnKit.Regression.Linear{coefficients: coefficients}, list) :: {:ok, list}
90+
91+
def predict(%Linear{coefficients: coefficients}, samples) do
92+
result = samples |> Enum.map(fn sample -> predict_sample(sample, coefficients) end)
93+
{:ok, result}
94+
end
95+
96+
@doc """
97+
Returns the coefficient of determination R^2 of the prediction
98+
99+
## Parameters
100+
101+
- predictor: %LearnKit.Regression.Linear{}
102+
103+
## Examples
104+
105+
iex> predictor |> LearnKit.Regression.Linear.score
106+
{:ok, 0.9876543209876543}
107+
108+
"""
109+
@spec score(%LearnKit.Regression.Linear{factors: factors, results: results, coefficients: coefficients}) :: {:ok, number}
110+
111+
def score(%Linear{factors: factors, results: results, coefficients: coefficients}) do
112+
{:ok, calculate_score(coefficients, factors, results)}
113+
end
114+
end
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
defmodule LearnKit.Regression.Linear.Fit do
2+
@moduledoc """
3+
Module for fit functions
4+
"""
5+
6+
alias LearnKit.Math
7+
8+
defmacro __using__(_opts) do
9+
quote do
10+
defp fit_data(factors, results) do
11+
beta = Math.correlation(factors, results) * Math.standard_deviation(results) / Math.standard_deviation(factors)
12+
alpha = Math.mean(results) - beta * Math.mean(factors)
13+
[alpha, beta]
14+
end
15+
end
16+
end
17+
end
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
defmodule LearnKit.Regression.Linear.Predict do
2+
@moduledoc """
3+
Module for fit functions
4+
"""
5+
6+
alias LearnKit.Math
7+
8+
defmacro __using__(_opts) do
9+
quote do
10+
defp predict_sample(sample, [alpha, beta]) do
11+
sample * beta + alpha
12+
end
13+
14+
defp calculate_score([], _, _), do: raise("There was no fit for model")
15+
16+
defp calculate_score(coefficients, factors, results) do
17+
1.0 - sum_of_squared_errors(coefficients, factors, results) / total_sum_of_squares(results)
18+
end
19+
20+
defp total_sum_of_squares(list) do
21+
mean_list = Math.mean(list)
22+
list
23+
|> Enum.map(fn x -> :math.pow(x - mean_list, 2) end)
24+
|> Enum.sum
25+
end
26+
27+
defp sum_of_squared_errors(coefficients, factors, results) do
28+
Enum.zip(factors, results)
29+
|> Enum.map(fn {xi, yi} -> :math.pow(prediction_error(coefficients, xi, yi), 2) end)
30+
|> Enum.sum
31+
end
32+
33+
defp prediction_error(coefficients, x, y) do
34+
y - predict_sample(x, coefficients)
35+
end
36+
end
37+
end
38+
end

0 commit comments

Comments
 (0)