Skip to content

Commit 973be81

Browse files
committed
add preprocessing normalization for data set
1 parent e585ec0 commit 973be81

6 files changed

Lines changed: 150 additions & 12 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
55
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
66

7+
## Unreleased
8+
### Added
9+
- preprocessing normalization for data set
10+
711
## [0.1.4] - 2018-12-17
812
### Modified
913
- errors conditions and tests for KNN

lib/learn_kit/knn.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ defmodule LearnKit.Knn do
55

66
defstruct data_set: []
77

8-
alias LearnKit.{Knn}
8+
alias LearnKit.Knn
99

1010
use Knn.Classify
1111

lib/learn_kit/knn/classify.ex

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -102,17 +102,6 @@ defmodule LearnKit.Knn.Classify do
102102
end)
103103
end
104104

105-
#defp calc_distance_between_points(acc, feature_from_data_set, feature, current_index, size) when current_index <= size do
106-
# Enum.at(feature_from_data_set, current_index) - Enum.at(feature, current_index)
107-
# |> :math.pow(2)
108-
# |> Math.summ(acc)
109-
# |> calc_distance_between_points(feature_from_data_set, feature, current_index + 1, size)
110-
#end
111-
112-
#defp calc_distance_between_points(acc, _, _, _, _) do
113-
# :math.sqrt(acc)
114-
#end
115-
116105
defp calc_feature_weight(weight, distance) do
117106
case weight do
118107
"uniform" -> 1

lib/learn_kit/preprocessing.ex

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
defmodule LearnKit.Preprocessing do
2+
@moduledoc """
3+
Module for data preprocessing
4+
"""
5+
6+
alias LearnKit.Preprocessing
7+
8+
use Preprocessing.Normalize
9+
10+
@type row :: [number]
11+
@type matrix :: [row]
12+
13+
@doc """
14+
Normalization of data set
15+
16+
## Parameters
17+
18+
- features: list of features for normalization
19+
- options: keyword list with options
20+
21+
## Options
22+
23+
- type: minimax/z_normalization, default is minimax, optional
24+
25+
## Examples
26+
27+
iex> LearnKit.Preprocessing.normalize([[1, 2], [3, 4], [5, 6]])
28+
[
29+
[0.0, 0.0],
30+
[0.5, 0.5],
31+
[1.0, 1.0]
32+
]
33+
34+
iex> LearnKit.Preprocessing.normalize([[1, 2], [3, 4], [5, 6]], [type: "z_normalization"])
35+
[
36+
[-1.224744871391589, -1.224744871391589],
37+
[0.0, 0.0],
38+
[1.224744871391589, 1.224744871391589]
39+
]
40+
41+
"""
42+
@spec normalize(matrix) :: matrix
43+
@spec normalize(matrix, list) :: matrix
44+
45+
def normalize(features) when is_list(features) do
46+
normalize(features, [type: "minimax"])
47+
end
48+
49+
def normalize(features, options) when is_list(features) and is_list(options) do
50+
options = Keyword.merge([type: "minimax"], options)
51+
case Keyword.get(options, :type) do
52+
"z_normalization" -> z_normalization(features)
53+
_ -> minimax_normalization(features)
54+
end
55+
end
56+
end
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
defmodule LearnKit.Preprocessing.Normalize do
2+
@moduledoc """
3+
Module for data normalization
4+
"""
5+
6+
alias LearnKit.Math
7+
8+
defmacro __using__(_opts) do
9+
quote do
10+
defp minimax_normalization(features) do
11+
features_by_index = Math.transpose(features)
12+
list_of_params =
13+
features_by_index
14+
|> Enum.map(fn list ->
15+
{
16+
Enum.min(list),
17+
Enum.max(list)
18+
}
19+
end)
20+
features_by_index
21+
|> Enum.with_index()
22+
|> Enum.map(fn {feature, index} ->
23+
params_for_point = Enum.at(list_of_params, index)
24+
range = elem(params_for_point, 1) - elem(params_for_point, 0)
25+
feature
26+
|> Enum.map(fn point ->
27+
(point - elem(params_for_point, 0)) / range
28+
end)
29+
end)
30+
|> Math.transpose()
31+
end
32+
33+
defp z_normalization(features) do
34+
features_by_index = Math.transpose(features)
35+
list_of_params =
36+
features_by_index
37+
|> Enum.map(fn list ->
38+
{
39+
Math.mean(list),
40+
Math.standard_deviation(list)
41+
}
42+
end)
43+
features_by_index
44+
|> Enum.with_index()
45+
|> Enum.map(fn {feature, index} ->
46+
params_for_point = Enum.at(list_of_params, index)
47+
feature
48+
|> Enum.map(fn point ->
49+
(point - elem(params_for_point, 0)) / elem(params_for_point, 1)
50+
end)
51+
end)
52+
|> Math.transpose()
53+
end
54+
end
55+
end
56+
end
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
defmodule LearnKit.PreprocessingTest do
2+
use ExUnit.Case
3+
4+
alias LearnKit.Preprocessing
5+
6+
describe "for invalid data" do
7+
test "use preprocessor with invalid data" do
8+
assert_raise FunctionClauseError, fn ->
9+
Preprocessing.normalize("")
10+
end
11+
end
12+
13+
test "use preprocessor with invalid options" do
14+
assert_raise FunctionClauseError, fn ->
15+
Preprocessing.normalize([[1, 2], [3, 4], [5, 6]], "")
16+
end
17+
end
18+
end
19+
20+
describe "for valid data" do
21+
test "normalize data set with minimax normalization" do
22+
result = Preprocessing.normalize([[1, 2], [3, 4], [5, 6]])
23+
24+
assert result == [[0.0, 0.0], [0.5, 0.5], [1.0, 1.0]]
25+
end
26+
27+
test "normalize data set with z normalization" do
28+
result = Preprocessing.normalize([[1, 2], [3, 4], [5, 6]], [type: "z_normalization"])
29+
30+
assert result == [[-1.224744871391589, -1.224744871391589], [0.0, 0.0], [1.224744871391589, 1.224744871391589]]
31+
end
32+
end
33+
end

0 commit comments

Comments
 (0)