Low commit activity in last 3 years
Count element frequencies from any enumerable with most-common and least-common queries, merge and subtract operations, percentage calculations, and Enumerable support.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies
 Project Readme

philiprehberger-counter

Tests Gem Version Last updated

Frequency counter with most-common, merge, and percentage operations

Requirements

  • Ruby >= 3.1

Installation

Add to your Gemfile:

gem "philiprehberger-counter"

Or install directly:

gem install philiprehberger-counter

Usage

require "philiprehberger/counter"

counter = Philiprehberger::Counter.new(%w[a b a c a b])
counter['a']            # => 3
counter.most_common(2)  # => [["a", 3], ["b", 2]]
counter.total           # => 6
counter.percentage('a') # => 50.0

Increment

counter = Philiprehberger::Counter.new
counter.increment('x')
counter.increment('x', 5)
counter['x']  # => 6

Merge and Subtract

a = Philiprehberger::Counter.new(%w[x x y])
b = Philiprehberger::Counter.new(%w[x z])
merged = a.merge(b)
merged['x']  # => 3

Enumerable

counter = Philiprehberger::Counter.new(%w[a b a])
counter.map { |key, count| "#{key}: #{count}" }
# => ["a: 2", "b: 1"]

Decrement and Reset

counter = Philiprehberger::Counter.new(%w[a a a b])
counter.decrement('a')     # => 2
counter.decrement('a', 2)  # => 0
counter.reset('b')         # removes 'b'
counter.reset              # clears all

Batch Update

counter = Philiprehberger::Counter.new
counter.update(%w[x y x z])         # count from enumerable
counter.update({ 'x' => 10 })       # add from hash
counter['x']                         # => 12

Delete

counter = Philiprehberger::Counter.new(%w[a a b c])
counter.delete('a')  # => 2 (removes key entirely)
counter.delete('z')  # => nil (key not present)

Min and Max

counter = Philiprehberger::Counter.new(%w[a b a c a b])
counter.max_count  # => ["a", 3]
counter.min_count  # => ["c", 1]
counter.mode       # => "a"

JSON Serialization

counter = Philiprehberger::Counter.new(%w[a b a])
json = counter.to_json              # => '{"a":2,"b":1}'
restored = Philiprehberger::Counter.from_json(json)
restored['a']                        # => 2

Weighted Sampling

counter = Philiprehberger::Counter.new(%w[a a a b])
counter.sample      # => "a" (weighted by count)
counter.sample(3)   # => ["a", "a", "b"] (array of weighted samples)

Keys and Values

counter = Philiprehberger::Counter.new(%w[a b a])
counter.keys    # => ["a", "b"]
counter.values  # => [2, 1]

Filtering

counter = Philiprehberger::Counter.new(%w[a a a b b c])
frequent = counter.filter_by_count(min: 2)
frequent.to_h  # => {"a" => 3, "b" => 2}

Entropy

counter = Philiprehberger::Counter.new(%w[a b c d])
counter.entropy  # => 2.0 (uniform 4-key distribution, bits)

skewed = Philiprehberger::Counter.new(%w[a a a a b])
skewed.entropy   # => ~0.7219 (less than uniform upper bound)

Diff Between Counters

before = Philiprehberger::Counter.new(%w[errors errors warnings])
after  = Philiprehberger::Counter.new(%w[errors errors errors warnings infos])

after.diff(before)
# => { "errors" => 1, "infos" => 1 }

Returns a Hash of { key => signed_delta } for every key whose count changed; equal counts are pruned. Useful for change detection between snapshots.

Unique Ratio

Ratio of unique keys to total observations — a simple diversity metric. 1.0 means every observation was unique; small values mean a few keys dominate.

Philiprehberger::Counter.new(%w[a b c d]).unique_ratio    # => 1.0
Philiprehberger::Counter.new(%w[a a a a]).unique_ratio    # => 0.25
Philiprehberger::Counter.new(%w[a a b b b c]).unique_ratio # => 0.5

API

Method Description
Counter.new(enumerable) Create a counter from an enumerable
#[key] Get count for a key
#increment(key, n) Increment count for a key
#most_common(n) Return n most common elements
#least_common(n) Return n least common elements
#total Sum of all counts
#merge(other) Merge two counters
#subtract(other) Subtract another counter
#diff(other) Hash of {key => signed_delta} for keys whose counts differ
#percentage(key) Percentage of key relative to total
#decrement(key, n) Decrement count for a key, floored at zero
#reset(key) Reset a specific key or clear all counts
#update(data) Batch update from a Hash or Enumerable
#delete(key) Remove a key entirely, returns count or nil
#max_count Key-count pair with highest count
#min_count Key-count pair with lowest count
#mode Most-common key (just the key, not the pair)
#to_json Serialize counter to JSON string
.from_json(str) Deserialize counter from JSON string
#sample(n) Weighted random sample based on counts
#keys Return all tracked keys
#values Return all count values
#filter_by_count(min:, max:) Filter entries by count range
#entropy Shannon entropy of the count distribution in bits
#unique_ratio Ratio of unique keys to total observations (size / total); 0.0 for empty counters
#to_h Convert to a plain hash
#size Number of unique keys

Development

bundle install
bundle exec rspec
bundle exec rubocop

Support

If you find this project useful:

Star the repo

🐛 Report issues

💡 Suggest features

❤️ Sponsor development

🌐 All Open Source Projects

💻 GitHub Profile

🔗 LinkedIn Profile

License

MIT