paragbaxi
paragbaxi

Reputation: 4223

python - scoring based on multiple data points

need guidance on easiest way to do this. i have multiple assets, each with multiple data points. I’m looking to assign a value to each asset based on decisions on each of these data points. As an example, each asset being a house, and data points including number of windows, garage door, broken fence, etc., each house would have a score.

Is there a recommended way of coding this besides the hundreds of if statements and adding/subtracting from a score?

Example of how I plan to do this

def score_house(house):
    # score house
    score = 0
    if (house.windows > 2): score += 10
    if (house.garage): score += 10
    if (house.fence == 'broken'): score += 80
    return score

Upvotes: 1

Views: 1177

Answers (2)

constt
constt

Reputation: 2320

I think you can also use the "chain of responsibility" pattern here:

The pattern allows multiple objects to handle the request without coupling sender class to the concrete classes of the receivers. The chain can be composed dynamically at runtime with any handler that follows a standard handler interface.

What is good about using this pattern is that you can define and extend different scorers in separate modules and combine them dynamically at runtime based on problem conditions. Here is how you can do it. First, define a parent scorer class:

from functools import reduce


class BaseScorer(object):

    def __init__(self):
        self._next_scorer = None

    def set_next(self, scorer):
        self._next_scorer = scorer

        return scorer

    def _call_next(self, house, score):

        if self._next_scorer is None:
            return score

        return self._next_scorer.score(house, score)

    def score(self, house, score=0):
        raise NotImplementedError

    @staticmethod
    def chain(scorers):
        reduce(lambda x, y: x.set_next(y), scorers)

        return scorers[0]

Then, define various scorer classes, for example:

class WindowScorer(BaseScorer):

    def score(self, house, score=0):

        if house.windows > 2:
            score = score + 10

        return self._call_next(house, score)


class GarageScorer(BaseScorer):

    def score(self, house, score=0):

        if house.garage:
            score = score + 10

        return self._call_next(house, score)


class FenceScorer(BaseScorer):

    def score(self, house, score=0):

        if house.fence == 'broken':
            score = score - 5

        return self._call_next(house, score)

And this is how it can be used:

scorer = BaseScorer.chain([
    WindowScorer(),
    GarageScorer(),
    FenceScorer()
])

house = House(windows=4, garage=True, fence='nice')
score = scorer.score(house)

Upvotes: 1

Alec
Alec

Reputation: 9575

I think your method is fine, and not worth the trouble to try and do something else. You might want to organize it more by defining a function like so:

def add_score(x, score): 
    score += x
    return score

And a dictionary like so:

sdict = {windows: 10, garage: 10, broken_fence: 10}

So that you can call your function like this:

def score_house(house):
    # score house
    score = 0
    if (house.windows > 2): add_score(sdict[windows])
    if (house.garage): add_score(sdict[garage])
    if (house.fence == 'broken'): add_score(sdict[broken_fence])
    return score

And easily be able to change scoring from a single dictionary.

You can also (and now that think about it, probably should) use Enums:

from enum import Enum
class Scoring(Enum):
   WINDOWS = 10
   ...

def score_house(house):
    # score house
    score = 0
    if (house.windows > 2): add_score(Scoring.WINDOWS)
    ...

Upvotes: 1

Related Questions