def test_can_normalize_distribution(self):
		p3 = ProbabilityDistribution({1: 1, (2, 3): 1}, normalize=False)
		self.assertFalse(p3.is_normalized)
		p3.normalize()
		self.assertTrue(p3.is_normalized)
		self.assertAlmostEqual(p3.p(1), 0.5)
		self.assertAlmostEqual(p3.mean, 1.75)
	def test_can_add_distributions_with_multiple_intersection_ranges(self):
		p4 = ProbabilityDistribution({
			(1, 4): 0.3,
			(4, 6): 0.6,
			(7, 8): 0.1
			})
		p5 = ProbabilityDistribution({
			(0, 2): 0.2,
			(2, 3): 0.3,
			(3, 9): 0.5 # intersects all ranges
			})
		p6 = p4 + p5
		p6.normalize()
		p6_prob_ranges_expected = {
			(0, 1): 0.05,
			(1, 2): 0.1,
			(2, 3): 0.2,
			(3, 4): (0.3/3 + 0.5/6)/2,
			(4, 6): (0.6 + 0.5/6*2)/2,
			(6, 7): (0.5/6)/2,
			(7, 8): (0.1 + 0.5/6)/2,
			(8, 9): (0.5/6)/2
		}
		# custom asserting since the dicts contain floats
		self.assertEqual(set(p6.prob_ranges.keys()), set(p6_prob_ranges_expected.keys()))
		for key in p6.prob_ranges.keys():
			# need AlmostEqual for floats
			self.assertAlmostEqual(p6.prob_ranges[key], p6_prob_ranges_expected[key])
	def test_can_initiate_distribution_with_duplicate_entries(self):
		prob_dist = [
			(4, 0.5),
			(4, 0.2),
			(5, 0.3)
		]
		p = ProbabilityDistribution(prob_dist)
		self.assertAlmostEqual(p.p(4), 0.7)
	def test_can_initiate_distribution_with_intersecting_ranges(self):
		prob_dist = [
			((4, 7), 0.3),
			((5, 8), 0.3),
			((5, 6), 0.4),
		]
		p = ProbabilityDistribution(prob_dist)
		self.assertAlmostEqual(p.p((5, 6)), 0.6)
		self.assertAlmostEqual(p.interval(0.6)[0], 5 + 1/6)
		self.assertAlmostEqual(p.interval(0.6)[1], 6.5)
	def test_can_initiate_distribution_with_list_of_values(self):
		prob_dist = [
			(4, 0.5),
			(5, 0.2),
			((5, 6), 0.3)
		]
		try:
			p = ProbabilityDistribution(prob_dist)
		except AttributeError as e:
			self.fail(f'Should not have raised error "{e}"')
		self.assertAlmostEqual(p.p(4), 0.5)
		self.assertAlmostEqual(p.p((5, 6)), 0.5)
		self.assertAlmostEqual(p.p((5.5, 6)), 0.15)
	def test_can_add_distributions_with_intersection_ranges(self):
		p4 = ProbabilityDistribution({
			(1, 3): 0.5,
			(4, 5): 0.5
			# mean: 3.25
			})
		p5 = ProbabilityDistribution({
			(2, 4): 0.5,
			(5, 6): 0.5
			# mean: 4.25
			})
		p6 = p4 + p5
		p6.normalize()
		self.assertEqual(p6.mean, 3.75)
		self.assertEqual(p6.p((2, 3)), 0.25)
	def test_can_sample_range(self):
		attributes = {
			'example_p': ProbabilityDistribution({
					 0: 0.01,
					 1: 0.01,
					 2.006: 0.01,
					 3: 0.01,
					 4: 0.01,
					 5: 0.01,
					 6: 0.88,
					 7: 0.01,
					 8: 0.01,
					 9: 0.01,
					10: 0.01,
					11: 0.01,
					12: 0.01,
				})
		}
		dwelling = Dwelling(attributes, self.mock_connection)

		dwelling.outputs = {
			'example': {
				'type': 'numrange',
				'sampling': True,
				'distribution': 'example_p'
			}
		}
		self.sampling_module.process(dwelling)
		# Should take 95% interval by default,
		# should round to 2 decimals.
		self.assertEqual(str(dwelling.attributes['example']), '[2.01, 10]')
Пример #8
0
    def get_building_code(self, construction_year):
        '''
		Get the building code applicable to buildings
		built in 'construction_year'. Only works for
		buildings built in or after 1992.
		'''
        df = self.building_code_r_values
        # Get the most recent version up to the the construction year.
        building_code = df[df.year <= construction_year].iloc[-1]
        return {
            'facade': ProbabilityDistribution({building_code['facade']: 1}),
            'roof': ProbabilityDistribution({building_code['roof']: 1}),
            'wall': ProbabilityDistribution({building_code['wall']: 1}),
            'floor': ProbabilityDistribution({building_code['floor']: 1}),
            'window': ProbabilityDistribution({building_code['window']: 1})
        }
	def test_string_representation_sorts_keys(self):
		p3 = ProbabilityDistribution({
			2: 0.5,
			1: 0.3,
			(0, 1): 0.2
		})
		# Note that it should be sorted
		expected_str = "ProbabilityDistribution({(0, 1): 0.2, 1: 0.3, 2: 0.5})"
		self.assertEqual(str(p3), expected_str)
	def test_can_copy(self):
		p3 = ProbabilityDistribution({
			1: 0.5,
			2: 0.2
		}, normalize=False)

		p4 = p3.copy()

		self.assertEqual(type(p4), ProbabilityDistribution)
		self.assertNotEqual(id(p3), id(p4))

		# Same as p3
		self.assertEqual(p4.mean, None)
		p4.normalize()
		self.assertAlmostEqual(p4.mean, 0.9/0.7)

		# Original object is unchanged.
		self.assertEqual(p3.mean, None)
	def setUp(self):
		self.p1_dict = {
			0: 0.1,
			1: 0.2,
			2: 0.3,
			3: 0.3,
			4: 0.1
			# mean: 2.1
		}
		self.p1 = ProbabilityDistribution(self.p1_dict)

		self.p2_dict = {
			(1, 2): 0.5,
			4: 0.2,
			(4, 5): 0.3
			# mean: 2.9
		}
		self.p2 = ProbabilityDistribution(self.p2_dict)
	def test_raises_NotImplementedError_when_adding_distributions_with_both_ranges(self):
		p3 = ProbabilityDistribution({
				(0, 1): 0.5,
				(2, 3): 0.5
			})
		try:
			p4 = p3 & p3
		except NotImplementedError:
			pass
	def test_can_sum_distributions(self):
		p3 = ProbabilityDistribution({
			1: 0.5,
			2: 0.5
			# mean: 1.5
			})
		p4 = sum([self.p1, self.p2, p3])
		p4.normalize()
		# mean: (2.1 + 2.9 + 1.5) / 3 = 2 1/6
		self.assertAlmostEqual(p4.mean, 2 + 1/6)
	def test_can_add_values_with_at_most_one_range(self):
		# This is different from adding the distributions,
		# but if
		# X ~ pd_x, Y ~ pd_y
		# and Z ~ pd_z,
		# then pd_x & pd_y ~ pd_z.
		# TODO: consider whether switching '+' and '&'
		# makes more sense?

		# flipping a coin
		coin = ProbabilityDistribution({
				0: 0.5,
				1: 0.5
			})
		p3 = ProbabilityDistribution({
				(0, 1): 0.5,
				(2, 3): 0.5
			})
		p4 = coin & p3
		self.assertEqual(type(p4), ProbabilityDistribution)
		self.assertEqual(p4.mean, coin.mean + p3.mean)
		self.assertEqual(p4.p(0), 0)
		self.assertEqual(p4.p((0, 1)), 0.25)
	def test_can_add_number(self):
		p3 = ProbabilityDistribution({
				5: 0.2,
				(0, 1): 0.3,
				(2, 3): 0.5
			})
		p4 = 2 & p3
		self.assertEqual(type(p4), ProbabilityDistribution)
		self.assertEqual(p4.mean, p3.mean + 2)
		self.assertEqual(p4.p(7), 0.2)
		self.assertEqual(p4.p((4, 5)), 0.5)
		p5 = p3 & 2
		self.assertEqual(type(p5), ProbabilityDistribution)
		self.assertEqual(p5.mean, p3.mean + 2)
		self.assertEqual(p5.p(7), 0.2)
		self.assertEqual(p5.p((4, 5)), 0.5)
	def test_can_pad_with_zero(self):
		# probability sums up to 0.7
		p3 = ProbabilityDistribution({
			1: 0.5,
			2: 0.2
			}, normalize=False)
		# sanity check
		self.assertAlmostEqual(p3.p(0), 0)
		self.assertEqual(p3.mean, None)
		p3.pad()
		self.assertAlmostEqual(p3.p(0), 0.3)
		self.assertEqual(p3.mean, 0.9)
	def test_can_sample_distribution(self):
		attributes = {
			'example_p': ProbabilityDistribution({
					1: 0.5,
					2: 0.5
				})
		}
		dwelling = Dwelling(attributes, self.mock_connection)

		dwelling.outputs = {
			'example': {
				'type': 'double precision',
				'sampling': True,
				'distribution': 'example_p'
			}
		}
		self.sampling_module.process(dwelling)
		# Should take mean by default
		self.assertAlmostEqual(dwelling.attributes['example'], 1.5)
	def test_can_add_values(self):
		# This is different from adding the distributions,
		# but if
		# X ~ pd_x, Y ~ pd_y
		# and Z ~ pd_z,
		# then pd_x & pd_y ~ pd_z.
		# TODO: consider whether switching '+' and '&'
		# makes more sense?

		# flipping a coin
		coin = ProbabilityDistribution({
				0: 0.5,
				1: 0.5
			})
		# flipping two
		p4 = coin & coin
		self.assertEqual(type(p4), ProbabilityDistribution)
		self.assertEqual(p4.mean, 1)
		self.assertEqual(p4.p(0), 0.25)
		self.assertEqual(p4.p(0), 0.25)
Пример #19
0
    def get_base_dist(self, dwelling):
        construction_year = dwelling.attributes['bouwjaar']
        dwelling_type = dwelling.attributes['woningtype']

        # From 2006 onwards, we don't have the WoON
        # base distribution anymore,
        # so we use the building code.
        if construction_year >= 2006:
            base_dist = self.get_building_code(construction_year)

            if construction_year < 2012:
                # The building code before 2012 was not very good
                # for windows, even though modern windows probably
                # have double glazing.
                base_dist['window'] = self.glazing_r_values['double']

        # From 1992 onwards, we have the WoON distribution
        # that we modified so it matches the building code.
        elif construction_year >= 1992:
            base_dist = self.base_r_values_1992_2005[dwelling_type]
            base_dist['window'] = self.glazing_r_values['double']

        # Cutoff point: buildings from 1920 (usually) have cavity walls, so we modified the WoON base distributions for that.
        elif construction_year >= 1920:
            base_dist = self.base_r_values_1920_1991[dwelling_type]

            if construction_year >= 1974:
                base_dist['window'] = self.glazing_r_values['double']
            else:
                base_dist['window'] = self.glazing_r_values['single']

        # Buildings before 1920, they have no cavity walls
        else:
            base_dist = self.base_r_values_before_1920[dwelling_type]
            base_dist['window'] = self.glazing_r_values['single']

        # We start all distributions off, pretending there is no
        # cavity wall insulation at all.
        base_dist['cavity wall'] = ProbabilityDistribution({0: 1})

        return base_dist
	def test_raises_ValueError_when_calculating_intervals_on_not_normalized_distribution(self):
		p = ProbabilityDistribution({1: 0.5}, normalize=False)
		self.assertRaisesRegex(ValueError, 'Cannot compute interval\(\) for the ProbabilityDistribution since it is not normalized', p.interval, 0.5)
Пример #21
0
    def process_insulation_type(self, dwelling, insulation_type):

        base_dist = self.get_base_dist(dwelling)[insulation_type]

        construction_year = dwelling.attributes['bouwjaar']
        dwelling_type = dwelling.attributes['woningtype']

        if insulation_type == 'cavity wall':
            # Only buildings between 1920 and 1974
            # are eligible for cavity wall insulation upgrade:
            # older buildings have no cavity wall,
            # newer buildings already have an insulated cavity wall.
            if 1920 <= construction_year <= 1974:
                applicable_measure_years = range(2010, 2019 + 1)
            else:
                applicable_measure_years = []
        else:
            # We only have data available for 2010 to 2019,
            # and we assume a waiting period of
            # MIN_YEAR_MEASURE_AFTER_CONSTRUCTION
            # after construction before a measure gets taken.
            applicable_measure_years = range(
                max(
                    2010, construction_year +
                    self.MIN_YEAR_MEASURE_AFTER_CONSTRUCTION), 2019 + 1)

        if insulation_type in ['facade', 'floor', 'roof']:
            measures_r_values = [
                self.insulation_measures_r_values[year]
                for year in applicable_measure_years
            ]
        elif insulation_type == 'window':
            # The info on measures that we have is on
            # HR glazing.
            measures_r_values = [
                self.glazing_r_values['hr'].copy()
                for _ in applicable_measure_years
            ]
        elif insulation_type == 'cavity wall':
            measures_r_values = [
                self.cavity_wall_r_value for _ in applicable_measure_years
            ]
        else:
            raise NotImplementedError

        measure_prob_multiplier = self.dwelling_type_multipliers[dwelling_type]

        measures_prob = [
            measure_prob_multiplier *
            self.insulation_measures_p[self.insulation_measures_p.year ==
                                       year][insulation_type].values[0]
            for year in applicable_measure_years
        ]

        if len(applicable_measure_years) == 0:
            # No measures apply,
            # so no probability for increases.
            # We need to set this case specifically,
            # because else the sum() will return 0,
            # which can't be .pad()-ded.
            measures_dist = ProbabilityDistribution({0: 1})
        else:
            measures_dist = sum([
                measures_r_values[i] * measures_prob[i]
                for i in range(len(measures_r_values))
            ])

        if insulation_type in ['facade', 'floor', 'roof']:
            measures_dist.pad()
            # Add the increase of R-values in measures_dist
            # to the base distribution base_dist.
            return base_dist & measures_dist

        # With windows, the measure is not an addition to the
        # existing insulation, but a replacement.
        elif insulation_type == 'window':
            p_measures = sum(measures_prob)

            # 'measures_dist' already includes the absolute
            # probability of the measures, so doesn't need to
            # be multiplied by p_measures.
            if p_measures != 0:
                window_r_dist = (1 - p_measures) * base_dist + measures_dist
            else:
                window_r_dist = base_dist
            return window_r_dist
        elif insulation_type == 'cavity wall':
            measures_dist.pad()
            # This comes as an addon to regular facade insulation.
            return measures_dist
        else:
            raise NotImplementedError
	def test_can_multiply_distributions_with_ranges(self):
		p3 = ProbabilityDistribution({(0.5, 0.625): 1.0})
		p4 = p3 * 3
		self.assertAlmostEqual(p4.p((0.5, 0.625)), 3)
class TestProbabilityUtils(unittest.TestCase):

	def setUp(self):
		self.p1_dict = {
			0: 0.1,
			1: 0.2,
			2: 0.3,
			3: 0.3,
			4: 0.1
			# mean: 2.1
		}
		self.p1 = ProbabilityDistribution(self.p1_dict)

		self.p2_dict = {
			(1, 2): 0.5,
			4: 0.2,
			(4, 5): 0.3
			# mean: 2.9
		}
		self.p2 = ProbabilityDistribution(self.p2_dict)

	def test_can_get_probability(self):
		prob = self.p1.p(0)
		self.assertAlmostEqual(prob, 0.1)

	def test_can_get_probability_outside_definition(self):
		prob = self.p1.p(10)
		self.assertEqual(prob, 0)

	def test_can_get_probability_for_range(self):
		prob = self.p1.p([0, 2.5])
		self.assertAlmostEqual(prob, 0.6)

	def test_raises_valueerror_for_badly_defined_ranges(self):
		# contains only one value
		self.assertRaisesRegex(ValueError, 'Expected length of interval to be 2, but length is', self.p1.p, [0])
		# contains three values value
		self.assertRaisesRegex(ValueError, 'Expected length of interval to be 2, but length is', self.p1.p, [0, 1, 3])

	def test_can_get_mean(self):
		self.assertAlmostEqual(self.p1.mean, 2.1)

	def test_can_get_interval(self):
		interval = self.p1.interval(0.95)
		self.assertEqual(interval, (0, 4))
		interval = self.p1.interval(0.5)
		self.assertEqual(interval, (1, 3))
		# bordering
		interval = self.p1.interval(0.8)
		self.assertEqual(interval, (1, 3))
		# complete
		interval = self.p1.interval(1)
		self.assertEqual(interval, (0, 4))

	def test_raises_when_interval_value_wrong(self):
		self.assertRaisesRegex(ValueError, 'Confidence value for interval should be', self.p1.interval, 0)
		self.assertRaisesRegex(ValueError, 'Confidence value for interval should be', self.p1.interval, 1.2)

	def test_supports_ranges_for_probabilities(self):
		# only in a range
		self.assertEqual(self.p2.p(1), 0)
		# specified both in range and as point
		self.assertEqual(self.p2.p(4), 0.2)
		self.assertEqual(self.p2.p((4.5, 5)), 0.15)

	def test_supports_ranges_for_mean(self):
		self.assertEqual(self.p2.mean, 2.9)

	def test_supports_ranges_for_interval(self):
		self.assertEqual(self.p2.interval(0.95), (1.05, 4.916666666666667))

	def test_can_add_distributions(self):
		p3 = self.p1 + self.p2
		self.assertEqual(type(p3), ProbabilityDistribution)
		p3.normalize()
		self.assertEqual(p3.mean, 2.5)
		self.assertAlmostEqual(p3.p(4), 0.15)

	def test_can_add_distributions_with_intersection_ranges(self):
		p4 = ProbabilityDistribution({
			(1, 3): 0.5,
			(4, 5): 0.5
			# mean: 3.25
			})
		p5 = ProbabilityDistribution({
			(2, 4): 0.5,
			(5, 6): 0.5
			# mean: 4.25
			})
		p6 = p4 + p5
		p6.normalize()
		self.assertEqual(p6.mean, 3.75)
		self.assertEqual(p6.p((2, 3)), 0.25)

	def test_can_add_distributions_with_multiple_intersection_ranges(self):
		p4 = ProbabilityDistribution({
			(1, 4): 0.3,
			(4, 6): 0.6,
			(7, 8): 0.1
			})
		p5 = ProbabilityDistribution({
			(0, 2): 0.2,
			(2, 3): 0.3,
			(3, 9): 0.5 # intersects all ranges
			})
		p6 = p4 + p5
		p6.normalize()
		p6_prob_ranges_expected = {
			(0, 1): 0.05,
			(1, 2): 0.1,
			(2, 3): 0.2,
			(3, 4): (0.3/3 + 0.5/6)/2,
			(4, 6): (0.6 + 0.5/6*2)/2,
			(6, 7): (0.5/6)/2,
			(7, 8): (0.1 + 0.5/6)/2,
			(8, 9): (0.5/6)/2
		}
		# custom asserting since the dicts contain floats
		self.assertEqual(set(p6.prob_ranges.keys()), set(p6_prob_ranges_expected.keys()))
		for key in p6.prob_ranges.keys():
			# need AlmostEqual for floats
			self.assertAlmostEqual(p6.prob_ranges[key], p6_prob_ranges_expected[key])

	def test_can_sum_distributions(self):
		p3 = ProbabilityDistribution({
			1: 0.5,
			2: 0.5
			# mean: 1.5
			})
		p4 = sum([self.p1, self.p2, p3])
		p4.normalize()
		# mean: (2.1 + 2.9 + 1.5) / 3 = 2 1/6
		self.assertAlmostEqual(p4.mean, 2 + 1/6)

	def test_raises_when_adding_not_distribution(self):
		try:
			self.p1 + 1
		except TypeError:
			pass
		except Exception as e:
			self.fail(f'Should have raised TypeError, but raised {type(e)}')
		else:
			self.fail('Should have raised')

	def test_can_initiate_distribution_with_list_of_values(self):
		prob_dist = [
			(4, 0.5),
			(5, 0.2),
			((5, 6), 0.3)
		]
		try:
			p = ProbabilityDistribution(prob_dist)
		except AttributeError as e:
			self.fail(f'Should not have raised error "{e}"')
		self.assertAlmostEqual(p.p(4), 0.5)
		self.assertAlmostEqual(p.p((5, 6)), 0.5)
		self.assertAlmostEqual(p.p((5.5, 6)), 0.15)

	def test_can_initiate_distribution_with_duplicate_entries(self):
		prob_dist = [
			(4, 0.5),
			(4, 0.2),
			(5, 0.3)
		]
		p = ProbabilityDistribution(prob_dist)
		self.assertAlmostEqual(p.p(4), 0.7)

	def test_can_initiate_distribution_with_intersecting_ranges(self):
		prob_dist = [
			((4, 7), 0.3),
			((5, 8), 0.3),
			((5, 6), 0.4),
		]
		p = ProbabilityDistribution(prob_dist)
		self.assertAlmostEqual(p.p((5, 6)), 0.6)
		self.assertAlmostEqual(p.interval(0.6)[0], 5 + 1/6)
		self.assertAlmostEqual(p.interval(0.6)[1], 6.5)

	def test_can_multiply_distribution_by_number(self):
		p7 = self.p1 * 0.5
		self.assertEqual(type(p7), ProbabilityDistribution)
		# this is not a true ProbabilityDistribution, probabilities sum to 0.5
		self.assertAlmostEqual(p7.p(4), 0.05)

	def test_can_multiply_distributions_with_ranges(self):
		p3 = ProbabilityDistribution({(0.5, 0.625): 1.0})
		p4 = p3 * 3
		self.assertAlmostEqual(p4.p((0.5, 0.625)), 3)

	def test_can_multiply_distribution_left_hand(self):
		p7 = 0.5 * self.p1
		self.assertEqual(type(p7), ProbabilityDistribution)
		# this is not a true ProbabilityDistribution, probabilities sum to 0.5
		self.assertAlmostEqual(p7.p(4), 0.05)

	def test_can_normalize_distribution(self):
		p3 = ProbabilityDistribution({1: 1, (2, 3): 1}, normalize=False)
		self.assertFalse(p3.is_normalized)
		p3.normalize()
		self.assertTrue(p3.is_normalized)
		self.assertAlmostEqual(p3.p(1), 0.5)
		self.assertAlmostEqual(p3.mean, 1.75)

	def test_automatically_normalized_distribution_on_creation(self):
		p3 = ProbabilityDistribution({1: 1, (2, 3): 1})
		# No need to call .normalize() manually.
		self.assertAlmostEqual(p3.p(1), 0.5)
		self.assertAlmostEqual(p3.mean, 1.75)

	def test_raises_ValueError_when_calculating_intervals_on_not_normalized_distribution(self):
		p = ProbabilityDistribution({1: 0.5}, normalize=False)
		self.assertRaisesRegex(ValueError, 'Cannot compute interval\(\) for the ProbabilityDistribution since it is not normalized', p.interval, 0.5)

	def test_mean_is_None_for_not_normalized_distribution(self):
		p = ProbabilityDistribution({1: 0.5}, normalize=False)
		self.assertEqual(p.mean, None)

	def test_add_range_to_ranges_raises_on_wrong_input(self):
		# Get the function from an instance.
		add_range_to_ranges = self.p1.add_range_to_ranges
		# ranges is a list
		self.assertRaises(ValueError, add_range_to_ranges, [], {(0, 1): 1})
		# new_range_value is a list
		self.assertRaises(ValueError, add_range_to_ranges, {}, [])
		# empty new range
		self.assertRaises(ValueError, add_range_to_ranges, {}, {})

	def test_add_range_to_ranges_simple(self):
		add_range_to_ranges = self.p1.add_range_to_ranges
		ranges = {
			(0, 1): 0.5,
			(1, 2): 0.5
		}
		new_range_value = {
			(2, 3): 1
		}
		expected = {
			(0, 1): 0.5,
			(1, 2): 0.5,
			(2, 3): 1
		}
		add_range_to_ranges(ranges, new_range_value)
		self.assertEqual(ranges, expected)

	def test_add_range_to_ranges_one_intersection(self):
		add_range_to_ranges = self.p1.add_range_to_ranges
		ranges = {
			(0, 1): 0.5,
			(1, 2): 0.5
		}
		new_range_value = {
			(1, 3): 1
		}
		expected = {
			(0, 1): 0.5,
			(1, 2): 1,
			(2, 3): 0.5
		}
		add_range_to_ranges(ranges, new_range_value)
		self.assertEqual(ranges, expected)

	def test_add_range_to_ranges(self):
		# Extra test since this went wrong, although the other tests worked.
		add_range_to_ranges = self.p1.add_range_to_ranges
		prob_ranges = {(1, 3): 0.5, (4, 5): 0.5}
		prob_range = {(2, 4): 0.5}
		add_range_to_ranges(prob_ranges, prob_range)
		expected_ranges = {
			(1, 2): 0.25,
			(2, 3): 0.5,
			(3, 4): 0.25,
			(4, 5): 0.5
		}
		self.assertEqual(prob_ranges, expected_ranges)

	def test_split_range(self):
		split_range = self.p1.split_range
		test_range = (1, 4)
		points = [3, 2]
		self.assertEqual(split_range(test_range, points), [(1, 2), (2, 3), (3, 4)])
		test_range = (1, 4)
		points = [4, 2]
		self.assertEqual(split_range(test_range, points), [(1, 2), (2, 4)])

	def test_split_prob_range(self):
		# Extra test of values that didn't work out at first.
		split_prob_range = self.p1.split_prob_range
		prob_range = {
			(1, 4): 0.5
		}
		points = [3, 2]
		self.assertEqual(split_prob_range(prob_range, points), {
				(1, 2): 1/6,
				(2, 3): 1/6,
				(3, 4): 1/6
			})
		points = [4, 2]
		self.assertEqual(split_prob_range(prob_range, points), {
				(1, 2): 1/6,
				(2, 4): 2/6
			})

	def test_split_prob_range_extra(self):
		split_prob_range = self.p1.split_prob_range
		prob_range = {(1, 3): 0.5}
		points = [1, 2, 3, 4]
		expected_split_prob_range = {
			(1, 2): 0.25,
			(2, 3): 0.25
		}

		self.assertEqual(split_prob_range(prob_range, points), expected_split_prob_range)

	def test_split_range_extra(self):
		split_range = self.p1.split_range
		split = split_range((1,3), [1, 2, 3, 4])
		expected_split = [(1, 2), (2, 3)]
		self.assertEqual(split, expected_split)

	def test_merge_prob_ranges(self):
		merge_prob_ranges = self.p1.merge_prob_ranges
		prob_ranges_1 = {
			(1, 3): 0.5,
			(4, 5): 0.5
			}
		prob_ranges_2 = {
			(2, 4): 0.5,
			(5, 6): 0.5
		}
		merged_prob_ranges = merge_prob_ranges(prob_ranges_1, prob_ranges_2)
		merged_prob_ranges_expected = {
			(1, 2): 0.25,
			(2, 3): 0.5,
			(3, 4): 0.25,
			(4, 5): 0.5,
			(5, 6): 0.5
		}
		self.assertEqual(merged_prob_ranges, merged_prob_ranges_expected)

	def test_can_add_values(self):
		# This is different from adding the distributions,
		# but if
		# X ~ pd_x, Y ~ pd_y
		# and Z ~ pd_z,
		# then pd_x & pd_y ~ pd_z.
		# TODO: consider whether switching '+' and '&'
		# makes more sense?

		# flipping a coin
		coin = ProbabilityDistribution({
				0: 0.5,
				1: 0.5
			})
		# flipping two
		p4 = coin & coin
		self.assertEqual(type(p4), ProbabilityDistribution)
		self.assertEqual(p4.mean, 1)
		self.assertEqual(p4.p(0), 0.25)
		self.assertEqual(p4.p(0), 0.25)

	def test_can_add_values_with_at_most_one_range(self):
		# This is different from adding the distributions,
		# but if
		# X ~ pd_x, Y ~ pd_y
		# and Z ~ pd_z,
		# then pd_x & pd_y ~ pd_z.
		# TODO: consider whether switching '+' and '&'
		# makes more sense?

		# flipping a coin
		coin = ProbabilityDistribution({
				0: 0.5,
				1: 0.5
			})
		p3 = ProbabilityDistribution({
				(0, 1): 0.5,
				(2, 3): 0.5
			})
		p4 = coin & p3
		self.assertEqual(type(p4), ProbabilityDistribution)
		self.assertEqual(p4.mean, coin.mean + p3.mean)
		self.assertEqual(p4.p(0), 0)
		self.assertEqual(p4.p((0, 1)), 0.25)

	def test_raises_NotImplementedError_when_adding_distributions_with_both_ranges(self):
		p3 = ProbabilityDistribution({
				(0, 1): 0.5,
				(2, 3): 0.5
			})
		try:
			p4 = p3 & p3
		except NotImplementedError:
			pass

	def test_can_add_number(self):
		p3 = ProbabilityDistribution({
				5: 0.2,
				(0, 1): 0.3,
				(2, 3): 0.5
			})
		p4 = 2 & p3
		self.assertEqual(type(p4), ProbabilityDistribution)
		self.assertEqual(p4.mean, p3.mean + 2)
		self.assertEqual(p4.p(7), 0.2)
		self.assertEqual(p4.p((4, 5)), 0.5)
		p5 = p3 & 2
		self.assertEqual(type(p5), ProbabilityDistribution)
		self.assertEqual(p5.mean, p3.mean + 2)
		self.assertEqual(p5.p(7), 0.2)
		self.assertEqual(p5.p((4, 5)), 0.5)

	def test_can_pad_with_zero(self):
		# probability sums up to 0.7
		p3 = ProbabilityDistribution({
			1: 0.5,
			2: 0.2
			}, normalize=False)
		# sanity check
		self.assertAlmostEqual(p3.p(0), 0)
		self.assertEqual(p3.mean, None)
		p3.pad()
		self.assertAlmostEqual(p3.p(0), 0.3)
		self.assertEqual(p3.mean, 0.9)

	def test_can_copy(self):
		p3 = ProbabilityDistribution({
			1: 0.5,
			2: 0.2
		}, normalize=False)

		p4 = p3.copy()

		self.assertEqual(type(p4), ProbabilityDistribution)
		self.assertNotEqual(id(p3), id(p4))

		# Same as p3
		self.assertEqual(p4.mean, None)
		p4.normalize()
		self.assertAlmostEqual(p4.mean, 0.9/0.7)

		# Original object is unchanged.
		self.assertEqual(p3.mean, None)

	def test_filters_out_p_0_values(self):
		p3 = ProbabilityDistribution({
			1: 1,
			2: 0
		})
		self.assertEqual(list(p3.prob_points.keys()), [1])

	def test_string_representation_sorts_keys(self):
		p3 = ProbabilityDistribution({
			2: 0.5,
			1: 0.3,
			(0, 1): 0.2
		})
		# Note that it should be sorted
		expected_str = "ProbabilityDistribution({(0, 1): 0.2, 1: 0.3, 2: 0.5})"
		self.assertEqual(str(p3), expected_str)
	def test_filters_out_p_0_values(self):
		p3 = ProbabilityDistribution({
			1: 1,
			2: 0
		})
		self.assertEqual(list(p3.prob_points.keys()), [1])
	def test_automatically_normalized_distribution_on_creation(self):
		p3 = ProbabilityDistribution({1: 1, (2, 3): 1})
		# No need to call .normalize() manually.
		self.assertAlmostEqual(p3.p(1), 0.5)
		self.assertAlmostEqual(p3.mean, 1.75)
Пример #26
0
            'floor': 3.7,
            'window': 1 / 1.65
        }
    },

    # Data for average R-values for insulation
    # measures, from
    # https://www.rvo.nl/sites/default/files/2021/02/marktinformatie-isolatiematerialen-isolatiegas-en-hr-ketels-2010-2019.pdf.
    # Values for mineral and organic wool
    # for 2010-2012 have been corrected
    # to account for the different definition.
    'insulation_measures_r_values': {
        2010:
        ProbabilityDistribution([
            # (R-value, millions of m^2 sold)
            (2.4, 5.4),  # synthetic insulation material
            (2.35, 10.1)  # mineral and organic wools
        ]),
        2011:
        ProbabilityDistribution([(2.5, 7.1), (2.46, 11.4)]),
        2012:
        ProbabilityDistribution([(2.7, 5.1), (2.57, 10.5)]),
        2013:
        ProbabilityDistribution([(2.8, 7.2), (2.9, 9.1)]),
        2014:
        ProbabilityDistribution([(2.7, 9.2), (2.8, 10.1)]),
        2015:
        ProbabilityDistribution([(3.3, 11.1), (2.8, 12)]),
        2016:
        ProbabilityDistribution([(3.3, 15.3), (3, 13.9)]),
        2017:
	def test_mean_is_None_for_not_normalized_distribution(self):
		p = ProbabilityDistribution({1: 0.5}, normalize=False)
		self.assertEqual(p.mean, None)