easy_ml 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +270 -0
- data/Rakefile +12 -0
- data/app/models/easy_ml/model.rb +59 -0
- data/app/models/easy_ml/models/xgboost.rb +9 -0
- data/app/models/easy_ml/models.rb +5 -0
- data/lib/easy_ml/core/model.rb +29 -0
- data/lib/easy_ml/core/model_core.rb +181 -0
- data/lib/easy_ml/core/model_evaluator.rb +137 -0
- data/lib/easy_ml/core/models/hyperparameters/base.rb +34 -0
- data/lib/easy_ml/core/models/hyperparameters/xgboost.rb +19 -0
- data/lib/easy_ml/core/models/hyperparameters.rb +8 -0
- data/lib/easy_ml/core/models/xgboost.rb +10 -0
- data/lib/easy_ml/core/models/xgboost_core.rb +220 -0
- data/lib/easy_ml/core/models.rb +10 -0
- data/lib/easy_ml/core/tuner/adapters/base_adapter.rb +63 -0
- data/lib/easy_ml/core/tuner/adapters/xgboost_adapter.rb +50 -0
- data/lib/easy_ml/core/tuner/adapters.rb +10 -0
- data/lib/easy_ml/core/tuner.rb +105 -0
- data/lib/easy_ml/core/uploaders/model_uploader.rb +24 -0
- data/lib/easy_ml/core/uploaders.rb +7 -0
- data/lib/easy_ml/core.rb +9 -0
- data/lib/easy_ml/core_ext/pathname.rb +9 -0
- data/lib/easy_ml/core_ext.rb +5 -0
- data/lib/easy_ml/data/dataloader.rb +6 -0
- data/lib/easy_ml/data/dataset/data/preprocessor/statistics.json +31 -0
- data/lib/easy_ml/data/dataset/data/sample_info.json +1 -0
- data/lib/easy_ml/data/dataset/dataset/files/sample_info.json +1 -0
- data/lib/easy_ml/data/dataset/splits/file_split.rb +140 -0
- data/lib/easy_ml/data/dataset/splits/in_memory_split.rb +49 -0
- data/lib/easy_ml/data/dataset/splits/split.rb +98 -0
- data/lib/easy_ml/data/dataset/splits.rb +11 -0
- data/lib/easy_ml/data/dataset/splitters/date_splitter.rb +43 -0
- data/lib/easy_ml/data/dataset/splitters.rb +9 -0
- data/lib/easy_ml/data/dataset.rb +430 -0
- data/lib/easy_ml/data/datasource/datasource_factory.rb +60 -0
- data/lib/easy_ml/data/datasource/file_datasource.rb +40 -0
- data/lib/easy_ml/data/datasource/merged_datasource.rb +64 -0
- data/lib/easy_ml/data/datasource/polars_datasource.rb +41 -0
- data/lib/easy_ml/data/datasource/s3_datasource.rb +89 -0
- data/lib/easy_ml/data/datasource.rb +33 -0
- data/lib/easy_ml/data/preprocessor/preprocessor.rb +205 -0
- data/lib/easy_ml/data/preprocessor/simple_imputer.rb +403 -0
- data/lib/easy_ml/data/preprocessor/utils.rb +17 -0
- data/lib/easy_ml/data/preprocessor.rb +238 -0
- data/lib/easy_ml/data/utils.rb +50 -0
- data/lib/easy_ml/data.rb +8 -0
- data/lib/easy_ml/deployment.rb +5 -0
- data/lib/easy_ml/engine.rb +26 -0
- data/lib/easy_ml/initializers/inflections.rb +4 -0
- data/lib/easy_ml/logging.rb +38 -0
- data/lib/easy_ml/railtie/generators/migration/migration_generator.rb +42 -0
- data/lib/easy_ml/railtie/templates/migration/create_easy_ml_models.rb.tt +23 -0
- data/lib/easy_ml/support/age.rb +27 -0
- data/lib/easy_ml/support/est.rb +1 -0
- data/lib/easy_ml/support/file_rotate.rb +23 -0
- data/lib/easy_ml/support/git_ignorable.rb +66 -0
- data/lib/easy_ml/support/synced_directory.rb +134 -0
- data/lib/easy_ml/support/utc.rb +1 -0
- data/lib/easy_ml/support.rb +10 -0
- data/lib/easy_ml/trainer.rb +92 -0
- data/lib/easy_ml/transforms.rb +29 -0
- data/lib/easy_ml/version.rb +5 -0
- data/lib/easy_ml.rb +23 -0
- metadata +353 -0
data/lib/easy_ml/data.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require "rails/engine"
|
2
|
+
|
3
|
+
module EasyML
|
4
|
+
class Engine < Rails::Engine
|
5
|
+
isolate_namespace EasyML
|
6
|
+
|
7
|
+
initializer "easy_ml.inflections" do
|
8
|
+
require_relative "initializers/inflections"
|
9
|
+
end
|
10
|
+
|
11
|
+
initializer "easy_ml.setup_generators" do |app|
|
12
|
+
app.config.generators do |g|
|
13
|
+
g.templates.unshift File.expand_path("../templates", __dir__)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
generators_path = File.expand_path("railtie/generators", __dir__)
|
18
|
+
generators_dirs = Dir[File.join(generators_path, "**", "*.rb")]
|
19
|
+
generators_dirs.each { |file| require file }
|
20
|
+
|
21
|
+
config.after_initialize do
|
22
|
+
require_relative "../../app/models/easy_ml/model"
|
23
|
+
require_relative "../../app/models/easy_ml/models"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module EasyML
|
2
|
+
module Logging
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def log_method(method_name, message, verbose: false)
|
9
|
+
original_method = instance_method(method_name)
|
10
|
+
define_method(method_name) do |*args, &block|
|
11
|
+
log_message(message, verbose: verbose)
|
12
|
+
result = original_method.bind(self).call(*args, &block)
|
13
|
+
result
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def log_message(message, verbose: false)
|
19
|
+
if verbose
|
20
|
+
log_verbose(message)
|
21
|
+
else
|
22
|
+
puts message
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def log_verbose(message)
|
27
|
+
puts message if @verbose
|
28
|
+
end
|
29
|
+
|
30
|
+
def log_warning(message)
|
31
|
+
puts "\e[33mWARNING: #{message}\e[0m"
|
32
|
+
end
|
33
|
+
|
34
|
+
def log_info(message)
|
35
|
+
puts "\e[34mINFO: #{message}\e[0m"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# lib/railtie/generators/migration/migration_generator.rb
|
2
|
+
require "rails/generators"
|
3
|
+
require "rails/generators/active_record/migration"
|
4
|
+
|
5
|
+
module EasyML
|
6
|
+
module Generators
|
7
|
+
module Migration
|
8
|
+
class MigrationGenerator < Rails::Generators::Base
|
9
|
+
include Rails::Generators::Migration
|
10
|
+
namespace "easy_ml:migration"
|
11
|
+
|
12
|
+
# Set the source directory for templates
|
13
|
+
source_root File.expand_path("../../templates/migration", __dir__)
|
14
|
+
|
15
|
+
# Define the migration name
|
16
|
+
desc "Generates a migration for EasyMLModel with version and file for remote storage"
|
17
|
+
|
18
|
+
# Define the migration name; can be customized if needed
|
19
|
+
def self.migration_name
|
20
|
+
"create_easy_ml_models"
|
21
|
+
end
|
22
|
+
|
23
|
+
# Specify the next migration number
|
24
|
+
def self.next_migration_number(dirname)
|
25
|
+
if ActiveRecord.version < Gem::Version.new("7")
|
26
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
27
|
+
elsif ActiveRecord.timestamped_migrations
|
28
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
29
|
+
else
|
30
|
+
format("%.3d", (current_migration_number(dirname) + 1))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Generate the migration file using the template
|
35
|
+
def create_migration_file
|
36
|
+
migration_template "create_easy_ml_models.rb.tt",
|
37
|
+
"db/migrate/#{self.class.migration_name}.rb"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# lib/railtie/generators/templates/migration/create_easy_ml_models.rb.tt
|
2
|
+
class CreateEasyMLModels < ActiveRecord::Migration[6.0]
|
3
|
+
def change
|
4
|
+
create_table :easy_ml_models do |t|
|
5
|
+
t.string :name, null: false
|
6
|
+
t.boolean :is_live, default: false
|
7
|
+
t.string :version, null: false
|
8
|
+
t.string :ml_model
|
9
|
+
t.string :task
|
10
|
+
t.json :metrics, default: []
|
11
|
+
t.json :file, null: false
|
12
|
+
|
13
|
+
t.timestamps
|
14
|
+
|
15
|
+
t.index :created_at
|
16
|
+
t.index :name
|
17
|
+
t.index :version
|
18
|
+
t.index :is_live
|
19
|
+
t.index [:name, :version], unique: true
|
20
|
+
t.index [:name, :version, :is_live]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "active_support/duration"
|
2
|
+
|
3
|
+
module EasyML
|
4
|
+
module Support
|
5
|
+
module Age
|
6
|
+
def self.age(start_time, end_time, format: "human")
|
7
|
+
return nil unless start_time && end_time
|
8
|
+
|
9
|
+
age_duration = ActiveSupport::Duration.build((end_time - start_time).to_i)
|
10
|
+
age_parts = age_duration.parts
|
11
|
+
|
12
|
+
case format.to_s
|
13
|
+
when "human"
|
14
|
+
age_duration.inspect
|
15
|
+
when "days"
|
16
|
+
age_parts[:days]
|
17
|
+
when "hours"
|
18
|
+
age_parts[:hours]
|
19
|
+
when "minutes"
|
20
|
+
age_parts[:minutes]
|
21
|
+
when "integer"
|
22
|
+
age_duration.to_i
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
EST = ActiveSupport::TimeZone.new("America/New_York")
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module EasyML
|
2
|
+
class FileRotate
|
3
|
+
def initialize(directory, files_to_keep)
|
4
|
+
@directory = directory
|
5
|
+
@files_to_keep = files_to_keep
|
6
|
+
end
|
7
|
+
|
8
|
+
def cleanup(allowed_endings = %w[json])
|
9
|
+
return unless @directory.present?
|
10
|
+
|
11
|
+
allowed_patterns = allowed_endings.map { |ending| File.join(@directory, "**", "*#{ending}") }
|
12
|
+
files_to_check = allowed_patterns.empty? ? Dir.glob(File.join(@directory, "**/*")) : Dir.glob(allowed_patterns)
|
13
|
+
# Filter out directories
|
14
|
+
files_to_check = files_to_check.select { |file| File.file?(file) }
|
15
|
+
|
16
|
+
files_to_check.each do |file|
|
17
|
+
FileUtils.chown_R(`whoami`.chomp, "staff", file)
|
18
|
+
FileUtils.chmod_R(0o777, file)
|
19
|
+
File.delete(file) unless @files_to_keep.include?(file)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "active_support/concern"
|
2
|
+
require "fileutils"
|
3
|
+
|
4
|
+
module EasyML
|
5
|
+
module Support
|
6
|
+
module GitIgnorable
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
class_attribute :gitignore_attributes, default: {}
|
11
|
+
|
12
|
+
def self.set_gitignore_callbacks(attribute, &block)
|
13
|
+
gitignore_attributes[attribute] = block
|
14
|
+
|
15
|
+
prepend GitignoreInitializer
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module GitignoreInitializer
|
20
|
+
def initialize(options)
|
21
|
+
super
|
22
|
+
update_gitignore
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class_methods do
|
27
|
+
def gitignore(attribute, &block)
|
28
|
+
set_gitignore_callbacks(attribute, &block)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def update_gitignore
|
33
|
+
self.class.gitignore_attributes.each do |attribute, block|
|
34
|
+
attribute_value = send(attribute)
|
35
|
+
next if attribute_value.blank?
|
36
|
+
|
37
|
+
patterns = block ? block.call(attribute_value) : attribute_value
|
38
|
+
next if patterns.nil? || (patterns.respond_to?(:empty?) && patterns.empty?)
|
39
|
+
|
40
|
+
patterns = [patterns] unless patterns.is_a?(Array)
|
41
|
+
patterns = relativize(patterns)
|
42
|
+
gitignore_path = File.join(Dir.pwd, ".gitignore")
|
43
|
+
|
44
|
+
FileUtils.mkdir_p(File.dirname(gitignore_path))
|
45
|
+
FileUtils.touch(gitignore_path) unless File.exist?(gitignore_path)
|
46
|
+
|
47
|
+
existing_content = File.read(gitignore_path).split("\n")
|
48
|
+
new_patterns = patterns.reject { |pattern| existing_content.include?(pattern) }
|
49
|
+
next if new_patterns.empty?
|
50
|
+
|
51
|
+
new_content = (existing_content + new_patterns).join("\n").strip
|
52
|
+
File.write(gitignore_path, new_content)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
# Turn patterns like /Users/xyz/path/to/rails/x/**/* into: x/**/*
|
59
|
+
def relativize(patterns)
|
60
|
+
patterns.map do |pattern|
|
61
|
+
pattern.sub(%r{^#{Regexp.escape(Dir.pwd)}/}, "")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require "glue_gun"
|
2
|
+
|
3
|
+
module EasyML
|
4
|
+
module Support
|
5
|
+
class SyncedDirectory
|
6
|
+
include GlueGun::DSL
|
7
|
+
|
8
|
+
attribute :root_dir, :string
|
9
|
+
attribute :s3_bucket, :string
|
10
|
+
attribute :s3_prefix, :string
|
11
|
+
attribute :s3_access_key_id, :string
|
12
|
+
attribute :s3_secret_access_key, :string
|
13
|
+
|
14
|
+
def sync
|
15
|
+
return false if synced?
|
16
|
+
|
17
|
+
mk_dir
|
18
|
+
clean_dir!
|
19
|
+
download
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
def files
|
24
|
+
Dir.glob(File.join(root_dir, File.join(s3_prefix, "*.csv")))
|
25
|
+
end
|
26
|
+
|
27
|
+
def age(format: "human")
|
28
|
+
Age.age(last_updated_at, EST.now, format: format)
|
29
|
+
end
|
30
|
+
|
31
|
+
def stale?
|
32
|
+
!synced?
|
33
|
+
end
|
34
|
+
|
35
|
+
def synced?
|
36
|
+
return @synced unless @synced.nil?
|
37
|
+
|
38
|
+
@synced = calculate_synced
|
39
|
+
end
|
40
|
+
|
41
|
+
def last_updated_at
|
42
|
+
return nil if files.empty?
|
43
|
+
|
44
|
+
files.map { |file| File.mtime(file) }.max.in_time_zone(EST)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def mk_dir
|
50
|
+
FileUtils.mkdir_p(root_dir)
|
51
|
+
end
|
52
|
+
|
53
|
+
def clean_dir!
|
54
|
+
FileUtils.rm_rf(root_dir)
|
55
|
+
end
|
56
|
+
|
57
|
+
def s3
|
58
|
+
@s3 ||= begin
|
59
|
+
credentials = Aws::Credentials.new(s3_access_key_id, s3_secret_access_key)
|
60
|
+
Aws::S3::Client.new(credentials: credentials)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def download
|
65
|
+
s3.list_objects_v2(bucket: s3_bucket, prefix: s3_prefix).contents.each do |object|
|
66
|
+
next if object.key.end_with?("/") # skip folders
|
67
|
+
|
68
|
+
gzipped_file_path = File.join(root_dir, object.key)
|
69
|
+
FileUtils.mkdir_p(File.dirname(gzipped_file_path))
|
70
|
+
|
71
|
+
s3.get_object(
|
72
|
+
response_target: gzipped_file_path,
|
73
|
+
bucket: s3_bucket,
|
74
|
+
key: object.key
|
75
|
+
)
|
76
|
+
|
77
|
+
puts "Downloaded #{object.key} to #{gzipped_file_path}"
|
78
|
+
|
79
|
+
# Ungzip the file
|
80
|
+
ungzipped_file_path = ungzip_file(gzipped_file_path)
|
81
|
+
puts "Ungzipped to #{ungzipped_file_path}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def ungzip_file(gzipped_file_path)
|
86
|
+
ungzipped_file_path = gzipped_file_path.sub(/\.gz$/, "")
|
87
|
+
|
88
|
+
Zlib::GzipReader.open(gzipped_file_path) do |gz|
|
89
|
+
File.open(ungzipped_file_path, "wb") do |file|
|
90
|
+
file.write(gz.read)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
File.delete(gzipped_file_path) # Optionally delete the gzipped file after extraction
|
95
|
+
ungzipped_file_path
|
96
|
+
end
|
97
|
+
|
98
|
+
def expand_dir(dir)
|
99
|
+
return dir if dir.to_s[0] == "/"
|
100
|
+
|
101
|
+
Rails.root.join(dir)
|
102
|
+
end
|
103
|
+
|
104
|
+
def new_data_available?
|
105
|
+
return true if files.empty?
|
106
|
+
|
107
|
+
local_latest = last_updated_at
|
108
|
+
s3_latest = s3_last_updated_at
|
109
|
+
|
110
|
+
return false if s3_latest.nil?
|
111
|
+
|
112
|
+
s3_latest > local_latest
|
113
|
+
end
|
114
|
+
|
115
|
+
def calculate_synced
|
116
|
+
return false if age.nil?
|
117
|
+
|
118
|
+
!new_data_available?
|
119
|
+
end
|
120
|
+
|
121
|
+
def s3_last_updated_at
|
122
|
+
s3_latest = nil
|
123
|
+
|
124
|
+
s3.list_objects_v2(bucket: s3_bucket, prefix: s3_prefix).contents.each do |object|
|
125
|
+
next if object.key.end_with?("/")
|
126
|
+
|
127
|
+
s3_latest = [s3_latest, object.last_modified].compact.max
|
128
|
+
end
|
129
|
+
|
130
|
+
s3_latest.in_time_zone(EST)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
UTC = ActiveSupport::TimeZone.new("UTC")
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module EasyML
|
2
|
+
module Support
|
3
|
+
require_relative "support/age"
|
4
|
+
require_relative "support/git_ignorable"
|
5
|
+
require_relative "support/synced_directory"
|
6
|
+
require_relative "support/utc"
|
7
|
+
require_relative "support/est"
|
8
|
+
require_relative "support/file_rotate"
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module EasyML
|
2
|
+
class Trainer
|
3
|
+
# include GlueGun::DSL
|
4
|
+
# include EasyML::Logging
|
5
|
+
|
6
|
+
# define_attr :verbose, default: false
|
7
|
+
# define_attr :root_dir do |root_dir|
|
8
|
+
# File.join(root_dir, "trainer")
|
9
|
+
# end
|
10
|
+
|
11
|
+
# define_config :dataset do |config|
|
12
|
+
# config.define_option :default do |option|
|
13
|
+
# option.set_class EasyML::Data::Dataset
|
14
|
+
# option.define_attr :root_dir
|
15
|
+
# option.define_attr :target
|
16
|
+
# option.define_attr :batch_size
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
|
20
|
+
# define_config :model do |config|
|
21
|
+
# config.define_option :default do |option|
|
22
|
+
# option.set_class EasyML::Model
|
23
|
+
# option.define_attr :root_dir
|
24
|
+
# option.define_attr :name
|
25
|
+
# option.define_attr :hyperparameters
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
|
29
|
+
# def train
|
30
|
+
# log_info("Starting training process") if verbose
|
31
|
+
|
32
|
+
# dataset.refresh!
|
33
|
+
|
34
|
+
# log_info("Fitting model") if verbose
|
35
|
+
# dataset.train(split_ys: true) do |xs, ys|
|
36
|
+
# model.fit(xs, ys)
|
37
|
+
# end
|
38
|
+
|
39
|
+
# log_info("Saving model") if verbose
|
40
|
+
# model.save
|
41
|
+
|
42
|
+
# log_info("Training completed") if verbose
|
43
|
+
# end
|
44
|
+
|
45
|
+
# def evaluate
|
46
|
+
# log_info("Starting evaluation process") if verbose
|
47
|
+
|
48
|
+
# results = {}
|
49
|
+
|
50
|
+
# %i[train test valid].each do |split|
|
51
|
+
# log_info("Evaluating on #{split} set") if verbose
|
52
|
+
# predictions = []
|
53
|
+
# actuals = []
|
54
|
+
|
55
|
+
# dataset.send(split, split_ys: true) do |xs, ys|
|
56
|
+
# batch_predictions = model.predict(xs)
|
57
|
+
# predictions.concat(batch_predictions.to_a)
|
58
|
+
# actuals.concat(ys.to_a)
|
59
|
+
# end
|
60
|
+
|
61
|
+
# results[split] = calculate_metrics(predictions, actuals)
|
62
|
+
# end
|
63
|
+
|
64
|
+
# log_info("Evaluation completed") if verbose
|
65
|
+
# results
|
66
|
+
# end
|
67
|
+
|
68
|
+
# private
|
69
|
+
|
70
|
+
# def calculate_metrics(predictions, actuals)
|
71
|
+
# # Implement your metric calculations here
|
72
|
+
# # This is a placeholder and should be replaced with actual metric calculations
|
73
|
+
# {
|
74
|
+
# mse: mean_squared_error(predictions, actuals),
|
75
|
+
# mae: mean_absolute_error(predictions, actuals),
|
76
|
+
# r2: r_squared(predictions, actuals)
|
77
|
+
# }
|
78
|
+
# end
|
79
|
+
|
80
|
+
# def mean_squared_error(predictions, actuals)
|
81
|
+
# # Implement MSE calculation
|
82
|
+
# end
|
83
|
+
|
84
|
+
# def mean_absolute_error(predictions, actuals)
|
85
|
+
# # Implement MAE calculation
|
86
|
+
# end
|
87
|
+
|
88
|
+
# def r_squared(predictions, actuals)
|
89
|
+
# # Implement R-squared calculation
|
90
|
+
# end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module EasyML::Transforms
|
2
|
+
def self.included(base)
|
3
|
+
base.extend(ClassMethods)
|
4
|
+
end
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def transforms
|
8
|
+
@transforms ||= []
|
9
|
+
end
|
10
|
+
|
11
|
+
def transform(method_name)
|
12
|
+
transforms << method_name
|
13
|
+
end
|
14
|
+
|
15
|
+
def apply_transforms(df)
|
16
|
+
new.apply_transforms(df)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def missing_any?(list1, list2)
|
21
|
+
(list1 - list2).any?
|
22
|
+
end
|
23
|
+
|
24
|
+
def apply_transforms(df)
|
25
|
+
self.class.transforms.reduce(df) do |df, transform_method|
|
26
|
+
send(transform_method, df)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/easy_ml.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails"
|
4
|
+
require "active_record"
|
5
|
+
require "active_model"
|
6
|
+
require "active_support/all"
|
7
|
+
require "glue_gun"
|
8
|
+
require "numo/narray"
|
9
|
+
require "xgboost"
|
10
|
+
require_relative "easy_ml/version"
|
11
|
+
require_relative "easy_ml/engine"
|
12
|
+
|
13
|
+
module EasyML
|
14
|
+
class Error < StandardError; end
|
15
|
+
|
16
|
+
require_relative "easy_ml/support"
|
17
|
+
require_relative "easy_ml/core_ext"
|
18
|
+
require_relative "easy_ml/logging"
|
19
|
+
require_relative "easy_ml/data"
|
20
|
+
require_relative "easy_ml/transforms"
|
21
|
+
require_relative "easy_ml/core"
|
22
|
+
require_relative "easy_ml/trainer"
|
23
|
+
end
|