@@ -3,18 +3,39 @@ defmodule LearnKit.Knn.Classify do
33 Module for knn classify functions
44 """
55
6- alias LearnKit.Math
6+ alias LearnKit . { Preprocessing , Math }
77
88 defmacro __using__ ( _opts ) do
99 quote do
1010 defp prediction ( data_set , options ) do
1111 data_set
12+ |> filter_features_by_size ( Keyword . get ( options , :feature ) )
13+ |> check_normalization ( options )
1214 |> calc_distances_for_features ( options )
1315 |> sort_distances ( )
1416 |> select_closest_features ( options )
1517 |> check_zero_distance ( options )
1618 end
1719
20+ # knn uses only features with the same size as current feature
21+ defp filter_features_by_size ( data_set , current_feature ) do
22+ Enum . map ( data_set , fn { key , features } ->
23+ {
24+ key ,
25+ Enum . filter ( features , fn feature -> length ( feature ) == length ( current_feature ) end )
26+ }
27+ end )
28+ end
29+
30+ # normalize features
31+ defp check_normalization ( data_set , options ) do
32+ type = Keyword . get ( options , :normalization )
33+ case type do
34+ t when t in [ "minimax" , "z_normalization" ] -> normalize ( data_set , options , type )
35+ _ -> data_set
36+ end
37+ end
38+
1839 # select algorithm for prediction
1940 defp calc_distances_for_features ( data_set , options ) do
2041 case Keyword . get ( options , :algorithm ) do
@@ -23,14 +44,17 @@ defmodule LearnKit.Knn.Classify do
2344 end
2445 end
2546
47+ # sort distances
2648 defp sort_distances ( features ) do
2749 Enum . sort ( features , & ( elem ( & 1 , 0 ) <= elem ( & 2 , 0 ) ) )
2850 end
2951
52+ # take closest features
3053 defp select_closest_features ( features , options ) do
3154 Enum . take ( features , Keyword . get ( options , :k ) )
3255 end
3356
57+ # check existeness of current feature in data set
3458 defp check_zero_distance ( closest_features , options ) do
3559 { distance , label } = Enum . at ( closest_features , 0 )
3660 cond do
@@ -39,13 +63,33 @@ defmodule LearnKit.Knn.Classify do
3963 end
4064 end
4165
66+ # select best result based on weights
4267 defp select_best_label ( features , options ) do
4368 features
4469 |> calc_feature_weights ( options )
4570 |> accumulate_weight_of_labels ( [ ] )
4671 |> sort_result ( )
4772 end
4873
74+ # normalize each feature
75+ defp normalize ( data_set , options , type ) do
76+ coefficients = find_coefficients_for_normalization ( data_set , type )
77+ Enum . map ( data_set , fn { key , features } ->
78+ {
79+ key ,
80+ Enum . map ( features , fn feature -> Preprocessing . normalize_feature ( feature , coefficients , type ) end )
81+ }
82+ end )
83+ end
84+
85+ # find coefficients for normalization
86+ defp find_coefficients_for_normalization ( data_set , type ) do
87+ Enum . reduce ( data_set , [ ] , fn { _ , features } , acc ->
88+ Enum . reduce ( features , acc , fn feature , acc -> [ feature | acc ] end )
89+ end )
90+ |> Preprocessing . coefficients ( type )
91+ end
92+
4993 defp calc_feature_weights ( features , options ) do
5094 Enum . map ( features , fn feature ->
5195 Tuple . append ( feature , calc_feature_weight ( Keyword . get ( options , :weight ) , elem ( feature , 0 ) ) )
@@ -70,17 +114,10 @@ defmodule LearnKit.Knn.Classify do
70114 Enum . map ( keys , fn key ->
71115 data_set
72116 |> Keyword . get ( key )
73- |> filter_features_by_size ( current_feature )
74117 |> calc_distances_in_label ( current_feature , key )
75118 end )
76119 end
77120
78- defp filter_features_by_size ( features , current_feature ) do
79- Enum . filter ( features , fn feature ->
80- length ( feature ) == length ( current_feature )
81- end )
82- end
83-
84121 defp calc_distances_in_label ( features , current_feature , key ) do
85122 Enum . reduce ( features , [ ] , fn feature , acc ->
86123 distance = calc_distance_between_features ( feature , current_feature )
0 commit comments