Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cyclical Encoder + Column Ordering fix #208

Merged
merged 26 commits into from
Feb 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1df889f
updated protobuf definition and added dummy files and test for cyclic…
Dec 31, 2021
11cdeed
change cyclical encoder range to float from int
Jan 1, 2022
2c13a39
cyclical encoding done
Jan 1, 2022
6a74155
test encoder
Jan 2, 2022
391a9d8
encode test (half-done)
Jan 3, 2022
0a19610
fixed bugs and completed test cases for encoder
Jan 4, 2022
3be1a24
clean up redundant codes
Jan 4, 2022
789b4cc
added interface for cyclical encoder (incomplete: range not ready)
Jan 5, 2022
53af0cf
completed ui for cyclical encoder
Jan 6, 2022
1bbf457
update the generated codes to use same version as previously
Jan 7, 2022
d0e7712
resolve conflict and rebase
Jan 7, 2022
ba296f6
added test for encoder_op
Jan 10, 2022
001ff5c
added server test and function to compare json with tolerance given t…
Jan 10, 2022
f2dd4a1
update column ordering to remain if modified inplace, append if new
Jan 11, 2022
0593e39
fixed period type
Jan 12, 2022
651a598
improved hint on UI to explain cyclical encoding option
Jan 12, 2022
b83a194
improved test to better illustrate feature usage
Jan 13, 2022
2f7e70c
update wrong comment
Jan 14, 2022
1cd8e0c
fixes comments from PR
Jan 16, 2022
52675d5
updated md doc
Jan 17, 2022
1ce994e
change constants to private
Feb 6, 2022
9b9c66f
added UI validations for encoders
Feb 7, 2022
335fb4d
added e2e, updated doc with examples
Feb 8, 2022
8139dee
refactor var name
Feb 8, 2022
93ba9f3
added benchmark test for col update
Feb 9, 2022
93b40ba
refactor period to periodType
Feb 9, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions api/pkg/transformer/pipeline/encoder_op.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ func (e *EncoderOp) Execute(ctx context.Context, env *Environment) error {
return err
}
encoderImpl = ordinalEncoder
case *spec.Encoder_CyclicalEncoderConfig:
cyclicalEncoder, err := enc.NewCyclicalEncoder(encoderCfg.CyclicalEncoderConfig)
if err != nil {
return err
}
encoderImpl = cyclicalEncoder
default:
return fmt.Errorf("encoder spec have unexpected type %T", encoderCfg)
}
Expand Down
28 changes: 28 additions & 0 deletions api/pkg/transformer/pipeline/encoder_op_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,34 @@ func TestEncoderOp_Execute(t *testing.T) {
},
},
},
{
desc: "cyclical encoder config",
specs: []*spec.Encoder{
{
Name: "cyclicalEncoder",
EncoderConfig: &spec.Encoder_CyclicalEncoderConfig{
CyclicalEncoderConfig: &spec.CyclicalEncoderConfig{
EncodeBy: &spec.CyclicalEncoderConfig_ByEpochTime{
ByEpochTime: &spec.ByEpochTime{
Period: spec.PeriodType_MONTH,
},
},
},
},
},
},
env: &Environment{
symbolRegistry: symbol.NewRegistry(),
logger: logger,
},
expEncoder: map[string]interface{}{
"cyclicalEncoder": &encoder.CyclicalEncoder{
PeriodType: spec.PeriodType_MONTH,
Min: 0,
Max: 0,
},
},
},
{
desc: "multiple ordinal encoder config",
specs: []*spec.Encoder{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ transformerConfig:
suv: "1"
sedan: "2"
mpv: "3"
- name: "hourly_encode"
cyclicalEncoderConfig:
byRange:
min: 0
max: 60
transformations:
- tableTransformation:
inputTable: driver_table
Expand Down Expand Up @@ -48,7 +53,10 @@ transformerConfig:
- vehicle
- previous_vehicle
encoder: vehicle_mapping
- selectColumns: ["customer_id", "name", "rank", "rating", "vehicle", "previous_vehicle"]
- columns:
- test_time
encoder: hourly_encode
- selectColumns: ["customer_id", "name", "rank", "rating", "vehicle", "previous_vehicle", "test_time_x", "test_time_y"]
outputs:
- jsonOutput:
jsonTemplate:
Expand Down
40 changes: 37 additions & 3 deletions api/pkg/transformer/server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package server
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net"
Expand Down Expand Up @@ -273,13 +274,13 @@ func TestServer_PredictHandler_StandardTransformer(t *testing.T) {
headers: map[string]string{
"Content-Type": "application/json",
},
body: []byte(`{"drivers":[{"id":1,"name":"driver-1","vehicle":"motorcycle","previous_vehicle":"suv","rating":4},{"id":2,"name":"driver-2","vehicle":"sedan","previous_vehicle":"mpv","rating":3}],"customer":{"id":1111}}`),
body: []byte(`{"drivers":[{"id":1,"name":"driver-1","vehicle":"motorcycle","previous_vehicle":"suv","rating":4,"test_time":90},{"id":2,"name":"driver-2","vehicle":"sedan","previous_vehicle":"mpv","rating":3, "test_time": 15}],"customer":{"id":1111}}`),
},
expTransformedRequest: request{
headers: map[string]string{
"Content-Type": "application/json",
},
body: []byte(`{"instances":{"columns":["customer_id","name","rank","rating","vehicle","previous_vehicle"],"data":[[1111,"driver-2",2.5,0.5,2,3],[1111,"driver-1",-2.5,0.75,0,1]]}}`),
body: []byte(`{"instances":{"columns":["customer_id","name","rank","rating","vehicle","previous_vehicle", "test_time_x", "test_time_y"],"data":[[1111,"driver-2",2.5,0.5,2,3,0,1],[1111,"driver-1",-2.5,0.75,0,1,-1,0]]}}`),
},
modelResponse: response{
headers: map[string]string{
Expand Down Expand Up @@ -461,7 +462,10 @@ func TestServer_PredictHandler_StandardTransformer(t *testing.T) {

assert.Nil(t, err)

assert.JSONEq(t, string(tt.expTransformedRequest.body), string(body))
var expectedMap, actualMap map[string]interface{}
json.Unmarshal(tt.expTransformedRequest.body, &expectedMap)
json.Unmarshal(body, &actualMap)
assertJSONEqWithFloat(t, expectedMap, actualMap, 0.00000001)
assertHasHeaders(t, tt.expTransformedRequest.headers, r.Header)

w.Header().Add("Content-Type", "application/json")
Expand Down Expand Up @@ -507,6 +511,36 @@ func TestServer_PredictHandler_StandardTransformer(t *testing.T) {
}
}

//Function to assert that 2 JSONs are the same, with considerations of delta in float values
//inputs are map[string]interface{} converted from json, and delta which is th tolerance of error for the float
//function will first confirm that the number of items in map are same
//then it will iterate through each element in the map. If element is a map, it will work call itself recursively
//if it is a slice or an array, it will call itself for each element by first converting each into a map
//otherwise, if element is a basic type like float, int, string etc it will do the comparison to make sure they are the same
//For float type, a tolerance in differences "delta" is checked
func assertJSONEqWithFloat(t *testing.T, expectedMap map[string]interface{}, actualMap map[string]interface{}, delta float64) {

assert.Equal(t, len(expectedMap), len(actualMap))
for key, value := range expectedMap {
switch value.(type) {
case float64:
assert.InDelta(t, expectedMap[key], actualMap[key], delta)
case map[string]interface{}:
assertJSONEqWithFloat(t, expectedMap[key].(map[string]interface{}), actualMap[key].(map[string]interface{}), delta)
case []interface{}:
for i, v := range value.([]interface{}) {
exp := make(map[string]interface{})
act := make(map[string]interface{})
exp["v"] = v
act["v"] = actualMap[key].([]interface{})[i]
assertJSONEqWithFloat(t, exp, act, delta)
}
default:
assert.Equal(t, expectedMap[key], actualMap[key])
}
}
}

func createTransformerServer(transformerConfigPath string, feastClients feast.Clients, options *Options) (*Server, error) {
yamlBytes, err := ioutil.ReadFile(transformerConfigPath)
if err != nil {
Expand Down
40 changes: 17 additions & 23 deletions api/pkg/transformer/spec/common.pb.json.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading