Skip to content

Commit

Permalink
Version 1.0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
mrdimosthenis committed Sep 26, 2024
1 parent 732dd36 commit 6ff7b04
Show file tree
Hide file tree
Showing 31 changed files with 273 additions and 332 deletions.
11 changes: 6 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.0.0
- uses: erlef/setup-beam@v1.9.0
- uses: actions/checkout@v4
- uses: erlef/setup-beam@v1
with:
otp-version: "23.2"
gleam-version: "0.22.1"
- run: gleam format --check src test
otp-version: "27.0.0"
gleam-version: "1.5.1"
rebar3-version: "3"
- run: gleam deps download
- run: gleam test
- run: gleam format --check src test
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Turn data into functions! A simple and functional **machine learning** library,

```erlang
{deps, [
{emel, "1.0.0"}
{emel, "1.0.1"}
]}.
```

Expand All @@ -17,7 +17,7 @@ Turn data into functions! A simple and functional **machine learning** library,
```elixir
defp deps do
[
{:emel, "~> 1.0.0"}
{:emel, "~> 1.0.1"}
]
end
```
Expand Down
12 changes: 6 additions & 6 deletions gleam.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name = "emel"
version = "1.0.0"
version = "1.0.1"
licences = ["Apache-2.0"]
description = "Turn data into functions in the Erlang ecosystem"

Expand All @@ -14,10 +14,10 @@ user = "mrdimosthenis"
repo = "emel"

[dependencies]
gleam_stdlib = "~> 0.22"
minigen = "~> 0.0"
gleam_zlists = "~> 0.0"
gleam_synapses = "~> 1.0"
gleam_stdlib = "~> 0.40.0"
minigen = "~> 0.1.1"
gleam_zlists = "~> 1.0.0"
gleam_synapses = "~> 1.0.1"

[dev-dependencies]
gleeunit = "~> 0.6"
gleeunit = "~> 1.2.0"
23 changes: 11 additions & 12 deletions manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,18 @@
# You typically do not need to edit this file

packages = [
{ name = "gleam_json", version = "0.5.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "E42443C98AA66E30143C24818F2CEA801491C10CE6B1A5EDDF3FC4ABDC7601CB" },
{ name = "gleam_stdlib", version = "0.22.1", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "9C6C66EDAAFD097267089565488DEDFC111A0497DE9CB2C42248E26330CC48E9" },
{ name = "gleam_synapses", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_json", "minigen", "gleam_stdlib", "gleam_zlists"], otp_app = "gleam_synapses", source = "hex", outer_checksum = "B8D86A43EADC88D951AC9B3E40F667AFCDB30BA13485123E503041FBA542DE04" },
{ name = "gleam_zlists", version = "0.0.4", build_tools = ["gleam"], requirements = ["gleam_stdlib", "zlists"], otp_app = "gleam_zlists", source = "hex", outer_checksum = "A71998C938A90475051845960ABD73647DB9734088E78A58B0CDFC41B1486461" },
{ name = "gleeunit", version = "0.6.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "5BF486C3E135B7F5ED8C054925FC48E5B2C79016A39F416FD8CF2E860520EE55" },
{ name = "minigen", version = "0.0.3", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "minigen", source = "hex", outer_checksum = "F74D7B2BF5E05A493BF2048BB557F8FC08AA93EB5A6664FC66FD5A69C3A57B6A" },
{ name = "thoas", version = "0.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "B8E1F8C8FAD317C0B75239A9234CB093DE1FB8BE7BA3E41433FF80A0B3353973" },
{ name = "gleam_json", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "CB10B0E7BF44282FB25162F1A24C1A025F6B93E777CCF238C4017E4EEF2CDE97" },
{ name = "gleam_stdlib", version = "0.40.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "86606B75A600BBD05E539EB59FABC6E307EEEA7B1E5865AFB6D980A93BCB2181" },
{ name = "gleam_synapses", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib", "gleam_zlists", "minigen"], otp_app = "gleam_synapses", source = "hex", outer_checksum = "2097F516EF2131B6FCE3D2058C787632D7BFDEE3B91AFD3CBF669D43CE722BCE" },
{ name = "gleam_zlists", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "zlists"], otp_app = "gleam_zlists", source = "hex", outer_checksum = "DCA7EAAAB38C0BB3A16E7490EFF51DA0C2355A63B60BC3DD98752FDE96314B94" },
{ name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" },
{ name = "minigen", version = "0.1.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "minigen", source = "hex", outer_checksum = "AFD1E637A7F429C2C57438FB635CCCAD818AA9C39D6F6E1A5200E5952940E2FD" },
{ name = "zlists", version = "0.0.4", build_tools = ["mix", "rebar3"], requirements = [], otp_app = "zlists", source = "hex", outer_checksum = "BABC8B78984E4988DAFEB9E07A77F04D3F8EA208D49E60B6669DA43B0D2C2C4B" },
]

[requirements]
gleam_stdlib = "~> 0.22"
gleam_synapses = "~> 1.0"
gleam_zlists = "~> 0.0"
gleeunit = "~> 0.6"
minigen = "~> 0.0"
gleam_stdlib = { version = "~> 0.40.0" }
gleam_synapses = { version = "~> 1.0.1" }
gleam_zlists = { version = "~> 1.0.0" }
gleeunit = { version = "~> 1.2.0" }
minigen = { version = "~> 0.1.1" }
4 changes: 2 additions & 2 deletions src/emel/lazy/math/algebra.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import emel/utils/zlist as ut_zlist
import gleam/float
import gleam/int
import gleam/order.{Eq}
import gleam_zlists.{ZList} as zlist
import gleam_zlists.{type ZList} as zlist

pub fn first_minor(
matrix: ZList(ZList(Float)),
Expand All @@ -19,7 +19,7 @@ pub fn determinant(matrix: ZList(ZList(Float))) -> Float {
case zlist.count(matrix) {
0 -> 1.0
2 -> {
let [[a, b], [c, d]] = ut_zlist.to_list_of_lists(matrix)
let assert [[a, b], [c, d]] = ut_zlist.to_list_of_lists(matrix)
a *. d -. c *. b
}
_ ->
Expand Down
31 changes: 13 additions & 18 deletions src/emel/lazy/math/geometry.gleam
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import emel/utils/result as ut_res
import emel/utils/zlist as ut_zlist
import gleam/float
import gleam/function
import gleam_zlists.{ZList} as zlist
import gleam_zlists.{type ZList} as zlist

pub fn dot_product(x: ZList(Float), y: ZList(Float)) -> Float {
zlist.zip(x, y)
Expand All @@ -27,7 +26,7 @@ pub fn euclidean_distance(x: ZList(Float), y: ZList(Float)) -> Float {

pub fn magnitude(vector: ZList(Float)) -> Float {
vector
|> zlist.map(function.constant(0.0))
|> zlist.map(fn(_) { 0.0 })
|> euclidean_distance(vector)
}

Expand All @@ -46,20 +45,16 @@ pub fn centroid(points: ZList(ZList(Float))) -> ZList(Float) {
|> zlist.uncons
|> ut_res.unsafe_res
let #(s, n) =
zlist.reduce(
tl,
#(hd, 1.0),
fn(v, acc) {
let #(acc_s, acc_n) = acc
let next_acc_s =
zlist.zip(acc_s, v)
|> zlist.map(fn(t) {
let #(si, vi) = t
si +. vi
})
let next_acc_n = acc_n +. 1.0
#(next_acc_s, next_acc_n)
},
)
zlist.reduce(tl, #(hd, 1.0), fn(v, acc) {
let #(acc_s, acc_n) = acc
let next_acc_s =
zlist.zip(acc_s, v)
|> zlist.map(fn(t) {
let #(si, vi) = t
si +. vi
})
let next_acc_n = acc_n +. 1.0
#(next_acc_s, next_acc_n)
})
zlist.map(s, fn(x) { x /. n })
}
46 changes: 19 additions & 27 deletions src/emel/lazy/math/statistics.gleam
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import gleam/float
import gleam/int
import gleam_zlists.{ZList} as zlist
import gleam_zlists.{type ZList} as zlist

external fn log(Float) -> Float =
"math" "log"
@external(erlang, "math", "log")
pub fn log(x: Float) -> Float

fn log_base(x: Float, b: Float) -> Float {
log(x) /. log(b)
Expand All @@ -21,19 +21,15 @@ pub fn entropy(probability_values: ZList(Float)) -> Float {

pub fn classical_probability(observations: ZList(a), event: a) -> Float {
let #(numerator, denominator) =
zlist.reduce(
observations,
#(0.0, 0.0),
fn(ev, acc) {
let #(numer, denom) = acc
let next_numer = case ev == event {
True -> numer +. 1.0
False -> numer
}
let next_denom = denom +. 1.0
#(next_numer, next_denom)
},
)
zlist.reduce(observations, #(0.0, 0.0), fn(ev, acc) {
let #(numer, denom) = acc
let next_numer = case ev == event {
True -> numer +. 1.0
False -> numer
}
let next_denom = denom +. 1.0
#(next_numer, next_denom)
})
numerator /. denominator
}

Expand All @@ -42,16 +38,12 @@ pub fn mean_absolute_error(
observations: ZList(Float),
) -> Float {
let #(numerator, denominator) =
zlist.reduce(
zlist.zip(predictions, observations),
#(0.0, 0.0),
fn(p, acc) {
let #(v1, v2) = p
let #(numer, denom) = acc
let next_numer = numer +. float.absolute_value(v1 -. v2)
let next_denom = denom +. 1.0
#(next_numer, next_denom)
},
)
zlist.reduce(zlist.zip(predictions, observations), #(0.0, 0.0), fn(p, acc) {
let #(v1, v2) = p
let #(numer, denom) = acc
let next_numer = numer +. float.absolute_value(v1 -. v2)
let next_denom = denom +. 1.0
#(next_numer, next_denom)
})
numerator /. denominator
}
74 changes: 33 additions & 41 deletions src/emel/lazy/ml/decision_tree.gleam
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import emel/lazy/math/statistics as stats
import emel/utils/result as ut_res
import emel/utils/zlist as ut_zlist
import gleam/dict.{type Dict}
import gleam/int
import gleam/map.{Map}
import gleam/pair
import gleam_zlists.{ZList} as zlist
import gleam_zlists.{type ZList} as zlist

pub fn entropy_with_size(
dataset: ZList(Map(String, String)),
dataset: ZList(Dict(String, String)),
class_attr: String,
) -> #(Float, Int) {
let #(freqs, size) =
Expand All @@ -23,7 +23,7 @@ pub fn entropy_with_size(
}

pub fn feature_entropy(
dataset: ZList(Map(String, String)),
dataset: ZList(Dict(String, String)),
class_attr: String,
feature: String,
dataset_size: Int,
Expand All @@ -40,23 +40,23 @@ pub fn feature_entropy(
}

fn same_class(
rule: Map(String, String),
rule: Dict(String, String),
class_attr: String,
sub_dataset: ZList(Map(String, String)),
) -> ZList(Map(String, String)) {
sub_dataset: ZList(Dict(String, String)),
) -> ZList(Dict(String, String)) {
sub_dataset
|> zlist.head
|> ut_res.unsafe_res
|> ut_res.unsafe_get(class_attr)
|> map.insert(rule, class_attr, _)
|> dict.insert(rule, class_attr, _)
|> zlist.singleton
}

fn exhausted_attributes(
rule: Map(String, String),
rule: Dict(String, String),
class_attr: String,
grouped_by_class: ZList(#(String, ZList(Map(String, String)))),
) -> ZList(Map(String, String)) {
grouped_by_class: ZList(#(String, ZList(Dict(String, String)))),
) -> ZList(Dict(String, String)) {
grouped_by_class
|> ut_zlist.max_by(fn(t) {
t
Expand All @@ -66,16 +66,16 @@ fn exhausted_attributes(
})
|> ut_res.unsafe_res
|> pair.first
|> map.insert(rule, class_attr, _)
|> dict.insert(rule, class_attr, _)
|> zlist.singleton
}

fn unfold_rule(
rule: Map(String, String),
rule: Dict(String, String),
non_selected_attrs: ZList(String),
class_attr: String,
sub_dataset: ZList(Map(String, String)),
) -> ZList(Map(String, String)) {
sub_dataset: ZList(Dict(String, String)),
) -> ZList(Dict(String, String)) {
let grouped_by_class =
ut_zlist.group_by(sub_dataset, ut_res.unsafe_get(_, class_attr))
case zlist.count(grouped_by_class) {
Expand All @@ -89,24 +89,19 @@ fn unfold_rule(
let next_selected_attr =
non_selected_attrs
|> ut_zlist.max_by(fn(feature) {
entropy -. feature_entropy(
sub_dataset,
class_attr,
feature,
dataset_size,
)
entropy
-. feature_entropy(sub_dataset, class_attr, feature, dataset_size)
})
|> ut_res.unsafe_res
let next_non_selected_attrs =
zlist.filter(
non_selected_attrs,
fn(attr) { attr != next_selected_attr },
)
zlist.filter(non_selected_attrs, fn(attr) {
attr != next_selected_attr
})
sub_dataset
|> ut_zlist.group_by(ut_res.unsafe_get(_, next_selected_attr))
|> zlist.flat_map(fn(t) {
let #(feature_val, sub_group) = t
let next_rule = map.insert(rule, next_selected_attr, feature_val)
let next_rule = dict.insert(rule, next_selected_attr, feature_val)
unfold_rule(
next_rule,
next_non_selected_attrs,
Expand All @@ -120,32 +115,29 @@ fn unfold_rule(
}

pub fn decision_tree(
dataset: ZList(Map(String, String)),
dataset: ZList(Dict(String, String)),
attributes: ZList(String),
class: String,
) -> ZList(Map(String, String)) {
unfold_rule(map.new(), attributes, class, dataset)
) -> ZList(Dict(String, String)) {
unfold_rule(dict.new(), attributes, class, dataset)
}

pub fn classifier(
dataset: ZList(Map(String, String)),
dataset: ZList(Dict(String, String)),
discrete_attributes: ZList(String),
class: String,
) -> fn(Map(String, String)) -> String {
let all_rules: ZList(Map(String, String)) =
) -> fn(Dict(String, String)) -> String {
let all_rules: ZList(Dict(String, String)) =
decision_tree(dataset, discrete_attributes, class)
fn(item) {
all_rules
|> zlist.find(fn(rule) {
zlist.all(
discrete_attributes,
fn(feature) {
case map.get(rule, feature) {
Error(Nil) -> True
Ok(v) -> v == ut_res.unsafe_get(item, feature)
}
},
)
zlist.all(discrete_attributes, fn(feature) {
case dict.get(rule, feature) {
Error(Nil) -> True
Ok(v) -> v == ut_res.unsafe_get(item, feature)
}
})
})
|> ut_res.unsafe_res
|> ut_res.unsafe_get(class)
Expand Down
6 changes: 3 additions & 3 deletions src/emel/lazy/ml/k_means.gleam
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import emel/lazy/math/geometry
import emel/utils/result as ut_res
import emel/utils/zlist as ut_zlist
import emel/lazy/math/geometry
import gleam/pair
import gleam/set.{Set}
import gleam_zlists.{ZList} as zlist
import gleam/set.{type Set}
import gleam_zlists.{type ZList} as zlist

fn point_groups(
points: ZList(ZList(Float)),
Expand Down
Loading

0 comments on commit 6ff7b04

Please sign in to comment.