Class: GitFit::Dedup::Matcher
- Inherits:
-
Object
- Object
- GitFit::Dedup::Matcher
- Defined in:
- lib/git_fit/dedup/matcher.rb
Constant Summary collapse
- WEIGHTS =
{ time: 0.35, distance: 0.35, duration: 0.15, category: 0.15 }.freeze
- TRAJECTORY_WEIGHTS =
{ dtw_similarity: 0.30, overlap_score: 0.25, hausdorff_score: 0.25, distance_match: 0.20, }.freeze
Class Method Summary collapse
- .distance_score(d1, d2) ⇒ Object
- .duration_score(d1, d2) ⇒ Object
- .effective_duration(act) ⇒ Object
- .external_id_match(a, b) ⇒ Object
- .match(a, b) ⇒ Object
- .time_diff_abs(t1, t2) ⇒ Object
- .trajectory_score(scores) ⇒ Object
Class Method Details
.distance_score(d1, d2) ⇒ Object
63 64 65 66 67 68 69 |
# File 'lib/git_fit/dedup/matcher.rb', line 63 def self.distance_score(d1, d2) max = [d1, d2].max return nil if max <= 0 diff = (d1 - d2).abs return nil if diff > max * 0.15 1.0 - diff / (max * 0.15) end |
.duration_score(d1, d2) ⇒ Object
71 72 73 74 75 76 77 |
# File 'lib/git_fit/dedup/matcher.rb', line 71 def self.duration_score(d1, d2) max = [d1, d2].max return 0.0 if max <= 0 diff = (d1 - d2).abs return 0.0 if diff > max * 0.15 1.0 - diff / (max * 0.15) end |
.effective_duration(act) ⇒ Object
16 17 18 |
# File 'lib/git_fit/dedup/matcher.rb', line 16 def self.effective_duration(act) [act[:moving_time].to_f, act[:elapsed_time].to_f].max end |
.external_id_match(a, b) ⇒ Object
20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/git_fit/dedup/matcher.rb', line 20 def self.external_id_match(a, b) a_eid = a[:external_id] b_eid = b[:external_id] return nil if a_eid.nil? || b_eid.nil? return nil if a_eid.empty? || b_eid.empty? a_normalized = a_eid.sub(%r{^strava://activities/}, '') b_normalized = b_eid.sub(%r{^strava://activities/}, '') { confidence: 1.0, flag: 'derived', source: 'external_id' } if a_normalized == b_normalized end |
.match(a, b) ⇒ Object
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/git_fit/dedup/matcher.rb', line 32 def self.match(a, b) exact = external_id_match(a, b) return exact[:confidence] if exact t_diff = time_diff_abs(a[:start_date], b[:start_date]) max_diff = [300, (a[:elapsed_time] || 0).to_i, (b[:elapsed_time] || 0).to_i].max return 0.0 if t_diff > max_diff t_score = 1.0 - t_diff / max_diff.to_f d_score = distance_score(a[:distance].to_f, b[:distance].to_f) return 0.0 unless d_score dur_score = duration_score(effective_duration(a), effective_duration(b)) cat_score = a[:sport_category] == b[:sport_category] ? 1.0 : -0.67 result = t_score * WEIGHTS[:time] + d_score * WEIGHTS[:distance] + dur_score * WEIGHTS[:duration] + cat_score * WEIGHTS[:category] result.round(4) end |
.time_diff_abs(t1, t2) ⇒ Object
54 55 56 57 58 59 60 61 |
# File 'lib/git_fit/dedup/matcher.rb', line 54 def self.time_diff_abs(t1, t2) t1 = t1.to_s t2 = t2.to_s return 9999 if t1.empty? || t2.empty? (Time.parse(t1) - Time.parse(t2)).abs rescue StandardError 9999 end |
.trajectory_score(scores) ⇒ Object
79 80 81 82 83 84 85 86 87 |
# File 'lib/git_fit/dedup/matcher.rb', line 79 def self.trajectory_score(scores) return 0.0 if scores.nil? || scores.empty? weighted = TRAJECTORY_WEIGHTS.sum do |metric, weight| score = scores[metric] next 0.0 unless score.is_a?(Numeric) && score >= 0.0 && score <= 1.0 score * weight end weighted.round(4) end |