## Introduction

COVID-19, along with many respiratory viruses, is airborne [[1](https://www.science.org/doi/10.1126/science.abd9149)]. That means that they don't suddenly fall into the ground when emitted by an infectious person. They float in the air, and stay there for a long period of time, like smoke. The airborne nature of the virus has led to indoor air quality experts Richard Corsi & Jim Rosenthal to share their design of a simple Do-It-Yourself (DIY) air cleaner. The design involves taping a 20-inch-by-20-inch box fan to a box of air filters, which are called Corsi-Rosenthal (CR) boxes [[Clean Air Crew](https://cleanaircrew.org/boxfanfilterfaq/)]. A [UC Davis](https://energy.ucdavis.edu/wp-content/uploads/Case-Study_DIY-Portable-Air-Cleaners-083121.pdf) study shows that CR boxes are as effective in cleaning the air, while being a third of the cost of commercial air cleaners.

In 2020, some researchers studied the effectiveness of different cloth fabrics on filtration efficiency for masks [[[Konda](https://pubs.acs.org/doi/10.1021/acsnano.0c03252)], [[Hao](https://www.sciencedirect.com/science/article/pii/S1438463920305289?via%3Dihub)], [[Yang](https://docs.google.com/spreadsheets/d/17aJQn-zmhBWO9vlgfWQLAuRUNrxrUnE8Ftm2wUHlddI/edit#gid=0)]]. In Yang's research, they found that one layer of Rubbermaid microfiber has a filtration efficiency of about 46%, assuming an average inhalation rate of 60 liters per minute.

## Methods

### An equation for PM removal over time

Let $A$ represent the actual PM level, $t$ be the time in seconds, $d$ be the decaying factor (a fraction between 0 and 1), $n$ the filtration efficiency of the air cleaner, $c$ be the cubic feet per second output, and $v$ be the volume of the room (in cubic feet):

$$
\begin{equation}
\begin{aligned}
  A_{t+1} &= A_{t}d^{n \cdot c / v}
\end{aligned}
\end{equation}
$$

## Estimating the flow rate of the air cleaner

```python
import os
from pathlib import Path

import pandas as pd
import numpy as np
import graphviz
import seaborn as sns
import matplotlib.pyplot as plt

from linx.ds import BayesianNetwork as BN, \
    ConditionalProbabilityTable as CPT, \
    Query

from linx.data import ParquetData
from linx.infer import VariableElimination as VE
```

```python

```

```python
storage_path = Path(os.getcwd()) / "tmp"
```

```python
storage_path.mkdir(exist_ok=True)
```

```python
def miles_per_hour_to_feet_per_second(mph):
    """
    Miles per hour * 5280 feet / mile * 3600 sec / hour
    """
    return mph * 5280.0 / 3600
```
To estimate the flow rate of said air cleaner, I used a Kestrel 1000, a wind-speed measurement device. We can get flow rate by getting the wind speed, and then multiplying that with the surface area of the fan.

```python
def circle_area(diameter):
    """
    Returns area of circle
    """
    radius = diameter / 2
    return np.pi * radius ** 2
```

```python
miles_per_hour_to_feet_per_second(2)
```

    2.933333333333333

### surface_area_of_layer_in_sq_ft

```python
def surface_area_of_layer_in_sq_ft(
    layer,
    num_layers,
    no_air_flow_middle_diam_inches,
    with_air_flow_inches
):
    """
    Assuming 5 layers.

    1st layer is layer 0.

    Parameters:
        layer: integer

        num_layers: integer
            The number of circular layers.

        no_air_flow_middle_diam_inches: float
            This is the area of the circle that has no airflow, due to the design of
            the box fan (i.e. some circle with a box fan logo). Measured in inches.

        with_air_flow_inches: float
            When added to no_air_flow_middle_diam_inches, we get a diameter, whose
            circumference corresponds to the circle in which air is coming out of.

    Returns: float
        square-feet
    """
    num_inches_in_a_foot = 12

    if layer < 0 or layer > num_layers - 1:
        raise ValueError(f"Layer {layer} cannot be less than 0 or greater than 2.")

    engulfing_circle_area = circle_area(
        (
            no_air_flow_middle_diam_inches + (layer + 1) * with_air_flow_inches / num_layers
        ) / num_inches_in_a_foot
    )

    engulfed_circle_area = circle_area(
        (
            no_air_flow_middle_diam_inches + layer * with_air_flow_inches / num_layers
        ) / num_inches_in_a_foot
    )

    return (engulfing_circle_area - engulfed_circle_area)
```

```python
surface_area_of_layer_in_sq_ft(
    layer=0,
    num_layers=3,
    no_air_flow_middle_diam_inches=5.5,
    with_air_flow_inches=8.0
)
```

    0.1987736092549097

```python
np.pi * ((8.0 * 1/3 + 5.5) / (12 * 2))**2 - (np.pi * (5.5 / (2 * 12)) ** 2)
```

    0.1987736092549097

```python
surface_area_of_layer_in_sq_ft(
    layer=1,
    num_layers=3,
    no_air_flow_middle_diam_inches=5.5,
    with_air_flow_inches=8.0
)
```

    0.27634379823243543

```python
np.pi * ((8.0 * 2/3 + 5.5) / (12 * 2))**2 - np.pi * ((8.0 * 1/3 + 5.5) / (12 * 2))**2
```

    0.27634379823243543

```python
surface_area_of_layer_in_sq_ft(
    layer=2,
    num_layers=3,
    no_air_flow_middle_diam_inches=5.5,
    with_air_flow_inches=8.0
)
```

    0.35391398720996137

```python
np.pi * ((8.0 + 5.5) / (12 * 2))**2 - np.pi * ((8.0 * 2/3 + 5.5) / (12 * 2))**2
```

    0.35391398720996137

```python
airflow_areas = [
    surface_area_of_layer_in_sq_ft(
        layer=layer,
        num_layers=3,
        no_air_flow_middle_diam_inches=5.5,
        with_air_flow_inches=8.0
    )
    for layer in range(3)
]
```

```python
sum(airflow_areas)
```

    0.8290313946973065

```python
num_divisions = 12.0
surface_area_of_layer_in_sq_ft(
    layer=1,
    num_layers=3,
    no_air_flow_middle_diam_inches=5.5,
    with_air_flow_inches=8.0
) / num_divisions
```

    0.02302864985270295

### anemometer_readings

```python
anemometer_readings = pd.DataFrame([
    {
        'layer': 0,
        'group': 0,
        'reading': 0.4
    },
    {
        'layer': 0,
        'group': 0,
        'reading': 0.5
    },
    {
        'layer': 0,
        'group': 1,
        'reading': 1.2
    },
    {
        'layer': 0,
        'group': 1,
        'reading': 1.6
    },
    {
        'layer': 0,
        'group': 2,
        'reading': 1.1
    },
    {
        'layer': 0,
        'group': 2,
        'reading': 1.1
    },
    {
        'layer': 0,
        'group': 3,
        'reading': 1.3
    },
    {
        'layer': 0,
        'group': 3,
        'reading': 1.2
    },
    {
        'layer': 0,
        'group': 4,
        'reading': 0.3
    },
    {
        'layer': 0,
        'group': 4,
        'reading': 0.9
    },
    {
        'layer': 0,
        'group': 5,
        'reading': 0.3
    },
    {
        'layer': 0,
        'group': 5,
        'reading': 0.5
    },
    {
        'layer': 0,
        'group': 6,
        'reading': 0.3
    },
    {
        'layer': 0,
        'group': 6,
        'reading': 0.5
    },
    {
        'layer': 0,
        'group': 7,
        'reading': 0.5
    },
    {
        'layer': 0,
        'group': 7,
        'reading': 0.4
    },
    {
        'layer': 0,
        'group': 8,
        'reading': 1.3
    },
    {
        'layer': 0,
        'group': 8,
        'reading': 1.3
    },
    {
        'layer': 0,
        'group': 9,
        'reading': 1.5
    },
    {
        'layer': 0,
        'group': 9,
        'reading': 1.3
    },
    {
        'layer': 0,
        'group': 10,
        'reading': 1.2
    },
    {
        'layer': 0,
        'group': 10,
        'reading': 1.7
    },
    {
        'layer': 0,
        'group': 11,
        'reading': 1.6
    },
    {
        'layer': 0,
        'group': 11,
        'reading': 1.0
    },

    {
        'layer': 1,
        'group': 0,
        'reading': 2.8
    },
    {
        'layer': 1,
        'group': 0,
        'reading': 2.1
    },
    {
        'layer': 1,
        'group': 1,
        'reading': 2.7
    },
    {
        'layer': 1,
        'group': 1,
        'reading': 2.1
    },
    {
        'layer': 1,
        'group': 2,
        'reading': 2.0
    },
    {
        'layer': 1,
        'group': 2,
        'reading': 2.2
    },
    {
        'layer': 1,
        'group': 3,
        'reading': 3.0
    },
    {
        'layer': 1,
        'group': 3,
        'reading': 3.1
    },
    {
        'layer': 1,
        'group': 4,
        'reading': 2.4
    },
    {
        'layer': 1,
        'group': 4,
        'reading': 2.4
    },
    {
        'layer': 1,
        'group': 5,
        'reading': 2.5
    },
    {
        'layer': 1,
        'group': 5,
        'reading': 2.5
    },
    {
        'layer': 1,
        'group': 6,
        'reading': 1.6
    },
    {
        'layer': 1,
        'group': 6,
        'reading': 2.2
    },
    {
        'layer': 1,
        'group': 7,
        'reading': 2.5
    },
    {
        'layer': 1,
        'group': 7,
        'reading': 2.3
    },
    {
        'layer': 1,
        'group': 8,
        'reading': 2.4
    },
    {
        'layer': 1,
        'group': 8,
        'reading': 2.5
    },
    {
        'layer': 1,
        'group': 9,
        'reading': 2.4
    },
    {
        'layer': 1,
        'group': 9,
        'reading': 2.0
    },
    {
        'layer': 1,
        'group': 10,
        'reading': 2.0
    },
    {
        'layer': 1,
        'group': 10,
        'reading': 2.6
    },
    {
        'layer': 1,
        'group': 11,
        'reading': 2.4
    },
    {
        'layer': 1,
        'group': 11,
        'reading': 2.4
    },

    {
        'layer': 2,
        'group': 0,
        'reading': 1.2
    },
    {
        'layer': 2,
        'group': 0,
        'reading': 1.0
    },
    {
        'layer': 2,
        'group': 1,
        'reading': 1.1
    },
    {
        'layer': 2,
        'group': 1,
        'reading': 1.0
    },
    {
        'layer': 2,
        'group': 2,
        'reading': 1.2
    },
    {
        'layer': 2,
        'group': 2,
        'reading': 0.6
    },
    {
        'layer': 2,
        'group': 3,
        'reading': 1.2
    },
    {
        'layer': 2,
        'group': 3,
        'reading': 1.2
    },
    {
        'layer': 2,
        'group': 4,
        'reading': 1.7
    },
    {
        'layer': 2,
        'group': 4,
        'reading': 1.2
    },
    {
        'layer': 2,
        'group': 5,
        'reading': 0.6
    },
    {
        'layer': 2,
        'group': 5,
        'reading': 0.7
    },
    {
        'layer': 2,
        'group': 6,
        'reading': 0.4
    },
    {
        'layer': 2,
        'group': 6,
        'reading': 0.4
    },
    {
        'layer': 2,
        'group': 7,
        'reading': 1.6
    },
    {
        'layer': 2,
        'group': 7,
        'reading': 1.0
    },
    {
        'layer': 2,
        'group': 8,
        'reading': 1.1
    },
    {
        'layer': 2,
        'group': 8,
        'reading': 1.1
    },
    {
        'layer': 2,
        'group': 9,
        'reading': 1.0
    },
    {
        'layer': 2,
        'group': 9,
        'reading': 0.9
    },
    {
        'layer': 2,
        'group': 10,
        'reading': 0.5
    },
    {
        'layer': 2,
        'group': 10,
        'reading': 1.1
    },
    {
        'layer': 2,
        'group': 11,
        'reading': 0.9
    },
    {
        'layer': 2,
        'group': 11,
        'reading': 1.0
    }
])

```

### create_anemometer_cpt_df

```python
def create_anemometer_cpt_df(size):
    """
    Generates a dataframe that represents the conditional probility table for
    P(anemometer reading value | actual wind speed).

    Assumes that the measured feet per second of the anemometer is produced by
    Gaussian centered at the actual feet per second value, with a standard
    deviation of 0.1.

    Parameters
    """
    dfs = []
    minimum = 0
    maximum = 15
    for actual_cfs in np.arange(minimum, maximum, 0.1):
        cubic_feet_per_second = np.random.normal(actual_cfs, 0.1, size=size)

        df = pd.DataFrame({
            'actual_mps': round(actual_cfs, 2),
            'measured_mps': [round(cfs, 1) for cfs in cubic_feet_per_second],
            'value': 0
        })

        dfs.append(df)

    dfs = pd.concat(dfs)

    rates = (
        dfs.groupby(['actual_mps', 'measured_mps']).count()[['value']] \
        / dfs.groupby(['actual_mps']).count()[['value']]
    ).reset_index()

    valid_values = rates[(rates['measured_mps'] >= 0)]

    normalized = valid_values.set_index(['actual_mps', 'measured_mps'])[['value']] \
        / valid_values.groupby(['actual_mps']).sum()[['value']]

    return normalized.reset_index()
```

```python
anemometer_reading_df = create_anemometer_cpt_df(10000)
anemometer_reading_df
```

      actual_mps
      measured_mps
      value

      0
      0.0
      -0.0
      0.560558

      1
      0.0
      0.1
      0.344467

      2
      0.0
      0.2
      0.088295

      3
      0.0
      0.3
      0.006099

      4
      0.0
      0.4
      0.000581

      ...
      ...
      ...
      ...

      1307
      14.9
      14.8
      0.236700

      1308
      14.9
      14.9
      0.388300

      1309
      14.9
      15.0
      0.244300

      1310
      14.9
      15.1
      0.063500

      1311
      14.9
      15.2
      0.005400

1312 rows × 3 columns

### generate_flat_priors

```python
def generate_flat_priors(start, end, step, name, round_to=1):
    """
    Parameters:
        start: float
            The lower bound
        end: float
            The upper bound
        step: float
            The increment that will be taken to go from start to end
        round_to: integer
            We round the value so that we can compare floats more easily.
    """
    array = np.arange(start, end, step)

    proba = 1.0 / len(array)

    return pd.DataFrame(
        [{'value': proba, name: round(i, round_to)} for i in array]
    )

```

```python
priors = generate_flat_priors(0,5,0.1,'actual_mps')
```

### generate_cpts_for_anemometer_readings

```python
def generate_cpts_for_anemometer_readings(
    number_of_readings,
    anemometer_reading_df,
    given,
    outcome,
    storage_path
):
    """
    Generate conditional probability tables of anemometer readings.

    Parameters:
        number_of_readings: integer
        anemometer_reading_df: pd.DataFrame
        givens: list[str]
            The list of variables that are in the conditional.

    Returns: list[CPT]
    """
    cpts = []

    for i in range(number_of_readings):
        cpt = CPT(
            ParquetData(
                anemometer_reading_df.rename(
                    columns={
                        'measured_mps': f'{outcome}_{i}',
                        'actual_mps': f'{given}'
                    }
                ),
                storage_folder=storage_path
            ),
            outcomes=[f'{outcome}_{i}'],
            givens=[given]
        )
        cpts.append(cpt)

    return cpts

```

```python
cpts = generate_cpts_for_anemometer_readings(
    number_of_readings=2,
    anemometer_reading_df=anemometer_reading_df,
    given='actual_mps',
    outcome='measured_mps'
)
```

```python
priors
```

      value
      actual_mps

      0
      0.02
      0.0

      1
      0.02
      0.1

      2
      0.02
      0.2

      3
      0.02
      0.3

      4
      0.02
      0.4

      5
      0.02
      0.5

      6
      0.02
      0.6

      7
      0.02
      0.7

      8
      0.02
      0.8

      9
      0.02
      0.9

      10
      0.02
      1.0

      11
      0.02
      1.1

      12
      0.02
      1.2

      13
      0.02
      1.3

      14
      0.02
      1.4

      15
      0.02
      1.5

      16
      0.02
      1.6

      17
      0.02
      1.7

      18
      0.02
      1.8

      19
      0.02
      1.9

      20
      0.02
      2.0

      21
      0.02
      2.1

      22
      0.02
      2.2

      23
      0.02
      2.3

      24
      0.02
      2.4

      25
      0.02
      2.5

      26
      0.02
      2.6

      27
      0.02
      2.7

      28
      0.02
      2.8

      29
      0.02
      2.9

      30
      0.02
      3.0

      31
      0.02
      3.1

      32
      0.02
      3.2

      33
      0.02
      3.3

      34
      0.02
      3.4

      35
      0.02
      3.5

      36
      0.02
      3.6

      37
      0.02
      3.7

      38
      0.02
      3.8

      39
      0.02
      3.9

      40
      0.02
      4.0

      41
      0.02
      4.1

      42
      0.02
      4.2

      43
      0.02
      4.3

      44
      0.02
      4.4

      45
      0.02
      4.5

      46
      0.02
      4.6

      47
      0.02
      4.7

      48
      0.02
      4.8

      49
      0.02
      4.9

```python
bn = BN(
    cpts=cpts,
    priors=[
        CPT(
            ParquetData(
                priors,
                storage_folder=storage_path
            ),
            outcomes=['actual_mps']
        )
    ],
    graphviz_dag=graphviz.Digraph()
)
```

```python
bn.graphviz_dag
```

```python
factor = VE(
    network=bn,
    query=Query(
        outcomes=['actual_mps'],
        givens=[
            {
                'measured_mps_0': 1.0
            },
            {
                'measured_mps_1': 0.9
            },
        ]
    )
).compute()
```

    numerator: 0it [00:00, ?it/s]
    denominator: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 11.05it/s]

```python
factor.get_df()
```

      measured_mps_1
      measured_mps_0
      actual_mps
      value

      0
      0.9
      1.0
      0.6
      0.000005

      1
      0.9
      1.0
      0.7
      0.002127

      2
      0.9
      1.0
      0.8
      0.071956

      3
      0.9
      1.0
      0.9
      0.440202

      4
      0.9
      1.0
      1.0
      0.417247

      5
      0.9
      1.0
      1.1
      0.067161

      6
      0.9
      1.0
      1.2
      0.001291

      7
      0.9
      1.0
      1.3
      0.000011

```python
factor.get_df()[['actual_mps', 'value']].set_index('actual_mps').plot.bar()
```

### sample_total_air_delivery_rate

```python
def sample_total_air_delivery_rate(
    bayesian_network,
    anemometer_readings,
    num_sections_per_layer=12.0,
    num_feet_in_meters=3.28084,
    num_layers=3,
    no_air_flow_middle_diam_inches=5.5,
    with_air_flow_inches=8.0,
    sample_size=10000
):
    """
    Sample total air delivery rate.
    """
    total = np.zeros(sample_size)

    for layer in range(num_layers):

        sq_ft_section = surface_area_of_layer_in_sq_ft(
            layer=layer,
            num_layers=num_layers,
            no_air_flow_middle_diam_inches=no_air_flow_middle_diam_inches,
            with_air_flow_inches=with_air_flow_inches
        ) / num_sections_per_layer

        for group in range(int(num_sections_per_layer)):
            wind_speed_readings_for_layer_group = anemometer_readings[
                (anemometer_readings['layer'] == layer)
                & (anemometer_readings['group'] == group)
            ]

            reading_0 = wind_speed_readings_for_layer_group.iloc[0]['reading']
            reading_1 = wind_speed_readings_for_layer_group.iloc[1]['reading']
            query = Query(
                outcomes=['actual_mps'],
                givens=[
                    {
                        'measured_mps_0': reading_0
                    },
                    {
                        'measured_mps_1': reading_1
                    },
                ]
            )

            factor = VE(
                network=bayesian_network,
                query=query
            ).compute()

            result = factor.get_df()
            actual_windspeed_in_meters_per_second = \
                np.random.choice(
                    result['actual_mps'],
                    p=result['value'],
                    size=sample_size
                )

            actual_windspeed_in_feet_per_second = \
                actual_windspeed_in_meters_per_second * num_feet_in_meters

            # mean_windspeed = actual_windspeed_in_feet_per_second.mean()
            total = total + actual_windspeed_in_feet_per_second * sq_ft_section

    return pd.Series(total)

```

```python
adr_microfiber = sample_total_air_delivery_rate(
    anemometer_readings=anemometer_readings
)
fig, ax = plt.subplots(1,1)
ax.set_xlabel('Cubic Feet per Second')
pd.Series(adr_microfiber).plot.kde(title='Total Air Delivery Rate of CR Microfiber', ax=ax)
```

```python
anemometer_readings['reading'].mean()
```

    1.4416666666666667

Lit 3 matches, waited for a few minutes to ensure that the particle counter got a massive jump in readings (i.e. n0_3 jumped to 1800).

- Left at 8:01
- Turned on fan at 9:01
- Switched to Coway at 10:03
- Stopped data collection at 10:33

## Particle Counter Data

```python
particle_counter_data = pd.read_json("pms_7003_1.json", orient='records', lines=True).set_index('datetime')
```

```python
particle_counter_data
```

      pm1_0cf1
      pm2_5cf1
      pm10cf1
      pm1_0
      pm2_5
      pm10
      n0_3
      n0_5
      n1_0
      n2_5
      n5_0
      n10
      sensor_num

      datetime

      2021-12-18 19:53:27.899489
      0
      0
      0
      0
      0
      0
      3
      1
      1
      1
      0
      0
      0

      2021-12-18 19:53:28.817320
      0
      0
      0
      0
      0
      0
      3
      1
      1
      1
      0
      0
      0

      2021-12-18 19:53:29.655135
      0
      0
      0
      0
      0
      0
      3
      1
      1
      1
      0
      0
      0

      2021-12-18 19:53:30.573125
      0
      0
      0
      0
      0
      0
      6
      2
      1
      1
      0
      0
      0

      2021-12-18 19:53:31.410849
      0
      0
      0
      0
      0
      0
      6
      2
      1
      1
      0
      0
      0

      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...

      2021-12-18 22:33:46.151964
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0

      2021-12-18 22:33:47.005828
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0

      2021-12-18 22:33:47.923702
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0

      2021-12-18 22:33:48.777555
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0

      2021-12-18 22:33:49.695432
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0

10800 rows × 13 columns

```python
particle_counter_data['n0_3'].plot()
```

```python
particle_counter_data['n0_5'].plot()
```

```python
particle_counter_data['n1_0'].plot()
```

```python
particle_counter_data['n2_5'].plot()
```

```python

```

```python
fig, ax = plt.subplots()
particle_counter_data['pm1_0'].plot(ax=ax)
ax.axvline("2021-12-18 21:01", color='r')
ax.axvline("2021-12-18 22:03", color='g')
ax.axvline("2021-12-18 22:33", color='b')
```

```python
particle_reset = particle_counter_data.reset_index()
```

```python
def time_at(time_str, df):
    time_str_plus_one_second = pd.to_datetime(time_str) + pd.Timedelta('1 second')

    return (df['datetime'] >= time_str) & \
        (df['datetime'] < time_str_plus_one_second)

```

```python
pd.to_datetime('2021-12-18 21:01:00') + pd.Timedelta('1 second')
```

    Timestamp('2021-12-18 21:01:01')

```python
particle_reset.columns
```

    Index(['datetime', 'pm1_0cf1', 'pm2_5cf1', 'pm10cf1', 'pm1_0', 'pm2_5', 'pm10',
           'n0_3', 'n0_5', 'n1_0', 'n2_5', 'n5_0', 'n10', 'sensor_num'],
          dtype='object')

```python
particle_counter_data.loc['2021-12-18 22:03:00':'2021-12-18 22:06:38']
```

      pm1_0cf1
      pm2_5cf1
      pm10cf1
      pm1_0
      pm2_5
      pm10
      n0_3
      n0_5
      n1_0
      n2_5
      n5_0
      n10
      sensor_num

      datetime

      2021-12-18 22:03:00.394538
      8
      9
      10
      8
      9
      10
      861
      287
      59
      2
      0
      0
      0

      2021-12-18 22:03:01.312387
      6
      9
      9
      6
      9
      9
      798
      266
      61
      3
      0
      0
      0

      2021-12-18 22:03:02.166275
      6
      9
      9
      6
      9
      9
      798
      266
      61
      3
      0
      0
      0

      2021-12-18 22:03:03.084147
      6
      9
      9
      6
      9
      9
      792
      264
      63
      2
      0
      0
      0

      2021-12-18 22:03:03.921867
      6
      9
      9
      6
      9
      9
      792
      264
      63
      2
      0
      0
      0

      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...

      2021-12-18 22:06:35.376800
      1
      1
      1
      1
      1
      1
      234
      78
      9
      1
      1
      0
      0

      2021-12-18 22:06:36.230658
      1
      1
      1
      1
      1
      1
      234
      78
      9
      1
      1
      0
      0

      2021-12-18 22:06:37.148530
      1
      1
      1
      1
      1
      1
      234
      78
      9
      1
      1
      0
      0

      2021-12-18 22:06:38.002404
      1
      1
      1
      1
      1
      1
      234
      78
      9
      1
      1
      0
      0

      2021-12-18 22:06:38.920246
      0
      0
      1
      0
      0
      1
      180
      60
      4
      1
      1
      0
      0

248 rows × 13 columns

```python
particle_reset[
    (particle_reset['pm1_0'] == 0) &
    (particle_reset['datetime'] > '2021-12-18 22:00')
]
```

      datetime
      pm1_0cf1
      pm2_5cf1
      pm10cf1
      pm1_0
      pm2_5
      pm10
      n0_3
      n0_5
      n1_0
      n2_5
      n5_0
      n10
      sensor_num

      8955
      2021-12-18 22:06:38.920246
      0
      0
      1
      0
      0
      1
      180
      60
      4
      1
      1
      0
      0

      8956
      2021-12-18 22:06:39.774445
      0
      0
      1
      0
      0
      1
      180
      60
      4
      1
      1
      0
      0

      8957
      2021-12-18 22:06:40.691828
      0
      0
      1
      0
      0
      1
      162
      54
      4
      1
      1
      0
      0

      8958
      2021-12-18 22:06:41.529733
      0
      0
      1
      0
      0
      1
      162
      54
      4
      1
      1
      0
      0

      8959
      2021-12-18 22:06:42.463602
      0
      0
      1
      0
      0
      1
      213
      71
      3
      1
      1
      0
      0

      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...

      10795
      2021-12-18 22:33:46.151964
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0

      10796
      2021-12-18 22:33:47.005828
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0

      10797
      2021-12-18 22:33:47.923702
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0

      10798
      2021-12-18 22:33:48.777555
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0

      10799
      2021-12-18 22:33:49.695432
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0

1811 rows × 14 columns

```python
particle_reset.loc[8955:
```

```python
particle_reset[
    time_at('2021-12-18 21:04:00', df=particle_reset)
]
```

      datetime
      pm1_0cf1
      pm2_5cf1
      pm10cf1
      pm1_0
      pm2_5
      pm10
      n0_3
      n0_5
      n1_0
      n2_5
      n5_0
      n10
      sensor_num

      4737
      2021-12-18 21:04:00.179341
      47
      75
      79
      37
      59
      71
      5457
      1804
      426
      31
      5
      2
      0

```python
particle_reset[particle_reset['datetime'] > '2021-12-18 21:01']
```

      datetime
      pm1_0cf1
      pm2_5cf1
      pm10cf1
      pm1_0
      pm2_5
      pm10
      n0_3
      n0_5
      n1_0
      n2_5
      n5_0
      n10
      sensor_num

      4537
      2021-12-18 21:01:00.717187
      52
      84
      87
      40
      63
      76
      6219
      1969
      478
      40
      3
      1
      0

      4538
      2021-12-18 21:01:01.619060
      52
      86
      90
      40
      63
      78
      6234
      1969
      516
      46
      4
      2
      0

      4539
      2021-12-18 21:01:02.537044
      52
      84
      90
      40
      63
      78
      6195
      1957
      527
      42
      4
      2
      0

      4540
      2021-12-18 21:01:03.438805
      53
      86
      88
      40
      63
      76
      6354
      1984
      516
      38
      3
      2
      0

      4541
      2021-12-18 21:01:04.340676
      55
      86
      88
      41
      63
      76
      6426
      2009
      514
      34
      3
      2
      0

      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...

      10795
      2021-12-18 22:33:46.151964
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0

      10796
      2021-12-18 22:33:47.005828
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0

      10797
      2021-12-18 22:33:47.923702
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0

      10798
      2021-12-18 22:33:48.777555
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0

      10799
      2021-12-18 22:33:49.695432
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0

6263 rows × 14 columns

```python
particle_counter_data['pm1_0']
```

    datetime
    2021-12-18 19:53:27.899489    0
    2021-12-18 19:53:28.817320    0
    2021-12-18 19:53:29.655135    0
    2021-12-18 19:53:30.573125    0
    2021-12-18 19:53:31.410849    0
                                 ..
    2021-12-18 22:33:46.151964    0
    2021-12-18 22:33:47.005828    0
    2021-12-18 22:33:47.923702    0
    2021-12-18 22:33:48.777555    0
    2021-12-18 22:33:49.695432    0
    Name: pm1_0, Length: 10800, dtype: int64

```python
720/3.8
```

    189.47368421052633

```python
1.0 - np.exp((720.0 / (3.8 * 3600)) * np.log(4))
```

    -0.07569058622018243

```python
 ((720.0 / 180) * np.log(1.0/9)) / np.log(0.0003)
```

    1.083480390254976

```python
1 - np.exp(1215.0 / (233.0 * 30.0) * np.log(1-0.996))
```

    0.6170071333491278

```python
(720.0 / (3600.0 * 3.8)) * np.log(1.0/4) / np.log(1-0.617)
```

    0.07602513137100067

```python
1.0 / 2.718
```

    0.36791758646063283

```python
0.9**3600
```

    1.8751412236067268e-165

```python
np.exp(sum([np.log(0.9) for i in range(3600)]))
```

    1.8751412236327937e-165

### pm_actual

```python
def pm_actual(
    last_value,
    filtration_eff,
    decay,
    air_delivery_rate,
    room_volume,
):
    power = filtration_eff * air_delivery_rate / room_volume
    return last_value * (decay) ** power
```

### apply_recursive_pm

```python
def apply_recursive_pm(
    start_pm=100,
    filtration_eff=0.07,
    decay=0.383,
    air_delivery_rate=3.7,
    room_volume=720
):

    collection = [start_pm]

    for i in range(3599):

        new_value = pm_actual(
            last_value=collection[i],
            filtration_eff=filtration_eff,
            decay=decay,
            air_delivery_rate=air_delivery_rate,
            room_volume=room_volume,
        )

        collection.append(new_value)

    return pd.Series(collection)
```

```python
collection = apply_recursive_pm(
    start_pm=40,
    filtration_eff=0.08,
    decay=0.383,
    air_delivery_rate=3.7,
    room_volume=720
)
```

```python
pm_readings = particle_counter_data.reset_index()

```

### Produce simulated data using priors (prior predictive distribution)

```python
microfiber = pm_readings[
    (pm_readings['datetime'] > '2021-12-18 21:01') &
    (pm_readings['datetime'] <= '2021-12-18 22:01')
]

microfiber.loc[:, 'rank'] = microfiber.groupby(
    [
        microfiber['datetime'].dt.hour,
        microfiber['datetime'].dt.minute,
        microfiber['datetime'].dt.second
    ]
)['datetime'].transform('rank').copy()

only_one_reading = microfiber[microfiber['rank'] == 1].reset_index()
```

```python
only_one_reading['pm1_0'].plot()
```

```python
fig, ax = plt.subplots()
collection.plot(ax=ax)
only_one_reading['pm1_0'].plot(ax=ax, color='r')

```

```python
particle_counter_data['pm1_0'].plot()
```

```python
particle_counter_data['pm2_5'].plot()
```

```python
particle_counter_data['pm10'].plot()
```

```python
particle_counter_data['n5_0'].plot()
```

```python
particle_counter_data['n2_5'].plot()
```

```python
total_area = np.pi * ((5.5+8)/(2* 12))**2
no_airflow_area = np.pi * (5.5/(12 * 2))**2

avg_mps = anemometer_readings['reading'].mean()
avg_fps = avg_mps * 3.28
airflow_estimate = (total_area - no_airflow_area) * avg_fps
```

```python
se = anemometer_readings['reading'].std() / np.sqrt(anemometer_readings.shape[0])
(airflow_estimate - se, airflow_estimate + se)
```

    (3.8297710237678957, 4.010655219682763)

```python
total_area - no_airflow_area
```

    0.8290313946973065

## Calculate Air Delivery Rate of Coway AP-1512HH

```python
def cubic_feet_per_second(fs_of_layers, surface_areas_of_layers):
    """
    Parameters:
        fs_of_layers: np.array[float]
            Rate of airflow of each layer (Feet per second)
        surface_areas_of_layers: np.array[float]
            Surface area of each layer (Square feet)

    Returns: float
    """

    return (fs_of_layers * surface_areas_of_layers).sum()
```

```python
round(1.2345,1)
```

    1.2

```python
# def create_airflow_rate_table():
#     collection = []

#     surface_areas_of_layers = [
#         surface_area_of_layer_in_sq_ft(i) for i in range(5)
#     ]

#     for layer_0_fs in np.arange(0, 15, 0.1):
#         layer_0_fs = round(layer_0_fs, 2)

#         for layer_1_fs in np.arange(0, 15, 0.1):
#             layer_1_fs = round(layer_1_fs, 2)

#             for layer_2_fs in np.arange(0, 15, 0.1):
#                 layer_2_fs = round(layer_2_fs, 2)

#                 for layer_3_fs in np.arange(0, 15, 0.1):
#                     layer_3_fs = round(layer_3_fs, 2)

#                     for layer_4_fs in np.arange(0, 15, 0.1):
#                         layer_4_fs = round(layer_4_fs, 2)

#                         fs_of_layers = np.array([
#                             layer_0_fs,
#                             layer_1_fs,
#                             layer_2_fs,
#                             layer_3_fs,
#                             layer_4_fs
#                         ])

#                         cfs = cubic_feet_per_second(
#                             fs_of_layers,
#                             surface_areas_of_layers
#                         )

#                         for layer, fs in enumerate(fs_of_layers):
#                             dictionary['flayer {layer} feet per second'] = fs

#                         dictionary['cubic feet per second'] = round(cfs, 1)
#                         dictionary['value'] = 1

#                         collection.append(dictionary)

#                         for i in np.arange(0, 15, 0.1):

#     cfs = cubic_feet_per_second(
#         fs_of_layers,
#         surface_areas_of_layers
#     )

#     dictionary['airflow'] = cfs
```

```python
def least_significant_digit_power(num_string):
    if '.' in num_string:
        # There's a decimal point. Figure out how many digits are to the right
        # of the decimal point and negate that.
        return len(num_string.partition('.')[2])
    else:
        # No decimal point. Count trailing zeros.
        return len(num_string) - len(num_string.rstrip('0'))
```

```python
least_significant_digit_power("0.123456")
```

    6

### accuracy

```python
def accuracy(mps):
    """
    Parameters:
        Reading in meters per second (m/s)
    """

    # m / s * 3.28084 ft / m
    #mps = reading * 3.28084
    val_1 = 0.03 * mps
    val_2 = mps - int(mps)
    # 20 ft/min * 1 min / 60 sec = 0.33 ft / sec
    val_3 = 0.33

    return max(val_1, val_2, val_3)

```

```python
pd.Series(np.random.normal(10, 0.1, size=10000)).hist(bins=100)
```

### meters_to_feet

```python
def meters_to_feet(meters):
    return meters * 3.280 # feet per meter
```

### inches_to_feet

```python
def inches_to_feet(inches):
    return inches / 12.0
```

### coway_mps

```python
coway_mps = pd.DataFrame(
    [
        {'x': 0, 'y': 0, "measured_mps": 10.5},
        {'x': 0, 'y': 1, "measured_mps": 10.4},
        {'x': 0, 'y': 2, "measured_mps": 8.4},
        {'x': 1, 'y': 0, "measured_mps": 9.8},
        {'x': 1, 'y': 1, "measured_mps": 11.8},
        {'x': 1, 'y': 2, "measured_mps": 10.1},
        {'x': 2, 'y': 0, "measured_mps": 8.6},
        {'x': 2, 'y': 1, "measured_mps": 9.8},
        {'x': 2, 'y': 2, "measured_mps": 8.6},
        {'x': 3, 'y': 0, "measured_mps": 8.6},
        {'x': 3, 'y': 1, "measured_mps": 10.1},
        {'x': 3, 'y': 2, "measured_mps": 8.6},
    ]
)

```

### compute_cfps_rectangular_air_cleaner

```python

def compute_cfps_rectangular_air_cleaner(
    num_sub_sections,
    mps,
    width_in=4.75,
    height_in=3.75,
    num_sims=10000
):
    """
    Parameters:
        num_sub_sections: integer
            Number of sub-sections in the rectangular area.
        mps: float
            Flow rate in meters per second.
        total_area: float
            Total area in square inches.
        num_sims: integer
            Number of simulations
    """
    total = np.zeros(num_sims)
    sub_area = inches_to_feet(width_in) * inches_to_feet(height_in) / num_sub_sections

    for i, row in mps.iterrows():
        total += np.random.normal(
            meters_to_feet(row['measured_mps']),
            0.1,
            size=num_sims
        ) * sub_area

    return pd.Series(total)

```

```python
coway_cfps = compute_cfps_rectangular_air_cleaner(
    num_sub_sections = coway_mps.shape[0],
    mps=coway_mps,
    width_in=4.75,
    height_in=3.75,
)
```

```python
fig, ax = plt.subplots()
coway_cfps.plot.kde(
    ax=ax
)
ax.set_title("Coway AP-1512HH Air Delivery Rate")
ax.set_xlabel("Cubic Feet per Second")
```

This aligns with the manufacturer's claim that this is good for 360 sq. ft (to get 4 ACH):

```python
fig, ax = plt.subplots()
(coway_cfps * 3600).plot.kde(title='Coway AP-1512HH Air Delivery Rate per Hour', ax=ax)
ax.set_xlabel("Cubic Feet per Hour")
```

```python
cfps = pd.DataFrame({'cfps': coway_cfps.round(3)})
```

```python
cfps['value'] = 0
```

```python
def normalize(df, x='cfps'):
    return df.groupby(x).count() / df.groupby(x).count().sum()
```

```python
coway_cfps_normalized = normalize(cfps.rename(columns={'cfps': 'AP-1512HH cfps'}), x='AP-1512HH cfps')
coway_cfps_normalized
```

      value

      AP-1512HH cfps

      3.884
      0.0002

      3.885
      0.0002

      3.886
      0.0001

      3.887
      0.0006

      3.888
      0.0011

      3.889
      0.0033

      3.890
      0.0058

      3.891
      0.0130

      3.892
      0.0224

      3.893
      0.0373

      3.894
      0.0499

      3.895
      0.0704

      3.896
      0.0881

      3.897
      0.1038

      3.898
      0.1127

      3.899
      0.1108

      3.900
      0.1028

      3.901
      0.0818

      3.902
      0.0680

      3.903
      0.0480

      3.904
      0.0334

      3.905
      0.0210

      3.906
      0.0116

      3.907
      0.0071

      3.908
      0.0043

      3.909
      0.0017

      3.910
      0.0002

      3.911
      0.0001

      3.912
      0.0001

      3.913
      0.0002

```python
CPT(
    ParquetData(
        coway_cfps_normalized,
        storage_folder=storage_path
    ),
    outcomes=['AP-1512HH cfps']
)
```

    ConditionalProbabilityTable(
    	givens: [],
    	outcomes: ['AP-1512HH cfps']
    	df:

                     value
    AP-1512HH cfps
    3.884           0.0002
    3.885           0.0002
    3.886           0.0001
    3.887           0.0006
    3.888           0.0011
    3.889           0.0033
    3.890           0.0058
    3.891           0.0130
    3.892           0.0224
    3.893           0.0373
    3.894           0.0499
    3.895           0.0704
    3.896           0.0881
    3.897           0.1038
    3.898           0.1127
    3.899           0.1108
    3.900           0.1028
    3.901           0.0818
    3.902           0.0680
    3.903           0.0480
    3.904           0.0334
    3.905           0.0210
    3.906           0.0116
    3.907           0.0071
    3.908           0.0043
    3.909           0.0017
    3.910           0.0002
    3.911           0.0001
    3.912           0.0001
    3.913           0.0002)

TODO:

- generate conditional probability tables for actual -> measurement.
- Loop through from 0 to 100.
    - generate data
    - normalize
- handle case when generated data is below 0. Renormalize.

```python
def generate_df_for_actual_pm_to_measured_pm(
    actual_upper_limit=100,
    std=4,
    num_sims=10000
):
    """
    Parameters:
        actual_upper_limit: integer
            The highest number of particles to generate.

        std: numeric
            Standard deviation to use for generating measured_pm

        num_sims: integer
            The number of simulations.
    """
    concatables = []

    for u in range(actual_upper_limit):

        df = pd.DataFrame({
            'measured_pm': np.random.normal(u, std, size=num_sims)
        })
        df['actual_pm'] = u

        concatables.append(df)

    concat_df = pd.concat(concatables)

    concat_df['measured_pm'] = concat_df['measured_pm'].round()

    no_neg_df = concat_df[concat_df['measured_pm'] >= 0]
    no_neg_df['value'] = 0

    return no_neg_df.groupby(['actual_pm', 'measured_pm'])[['value']].count() / \
        no_neg_df.groupby(['actual_pm'])[['value']].count()
```

```python
measurement_df = generate_df_for_actual_pm_to_measured_pm()
```

    /var/folders/c3/b8f8shqn6sq34qlk8wt9rcqh0000gn/T/ipykernel_73159/951850254.py:33: SettingWithCopyWarning:
    A value is trying to be set on a copy of a slice from a DataFrame.
    Try using .loc[row_indexer,col_indexer] = value instead

    See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
      no_neg_df['value'] = 0

```python
def pm_actual_simplified(
    last_value,
    clean_air_delivery_rate,
    decay,
    room_volume,
):
    return clean_air_delivery_rate / room_volume
    return last_value * (decay) ** power
```

```python
def generate_cadr_df(
    adr_cfps_range=np.arange(0,12,0.1),
    filtration_eff_range=np.arange(0,1,0.01)
):
    collection = []

    df_1 = pd.DataFrame(
        {
            'adr_cfps': adr_cfps_range,
            'join_col': 1
        }
    )

    df_2 = pd.DataFrame(
        {
            'filtration_eff': filtration_eff_range,
            'join_col': 1
        }
    )

    merged = df_1.merge(df_2, on='join_col')

    merged['cadr_cfps'] = (merged['adr_cfps'] * merged['filtration_eff']).round(1)
    merged['value'] = 1.0

    return merged.drop(columns=['join_col'])
```

```python
cadr_df = generate_cadr_df()
```

```python
cadr_df['adr_cfps'] = cadr_df['adr_cfps'].round(1)
cadr_df['filtration_eff'] = cadr_df['filtration_eff'].round(2)
```

```python
def generate_power_df(
    cadr_cfps_range=np.arange(0,12,0.1),
    room_volume=list(range(690, 726))
):
    collection = []

    df_1 = pd.DataFrame(
        {
            'cadr_cfps': cadr_cfps_range,
            'join_col': 1
        }
    )

    df_2 = pd.DataFrame(
        {
            'room_vol': room_volume,
            'join_col': 1
        }
    )

    merged = df_1.merge(df_2, on='join_col')

    merged['power'] = (merged['cadr_cfps'] / merged['room_vol']).round(3)
    merged['value'] = 1.0

    return merged.drop(columns=['join_col'])
```

```python
power_df = generate_power_df()
power_df
```

      cadr_cfps
      room_vol
      power
      value

      0
      0.0
      690
      0.000
      1.0

      1
      0.0
      691
      0.000
      1.0

      2
      0.0
      692
      0.000
      1.0

      3
      0.0
      693
      0.000
      1.0

      4
      0.0
      694
      0.000
      1.0

      ...
      ...
      ...
      ...
      ...

      4315
      11.9
      721
      0.017
      1.0

      4316
      11.9
      722
      0.016
      1.0

      4317
      11.9
      723
      0.016
      1.0

      4318
      11.9
      724
      0.016
      1.0

      4319
      11.9
      725
      0.016
      1.0

4320 rows × 4 columns

```python
def pm_actual_simplified(
    last_value,
    clean_air_delivery_rate,
    decay,
    room_volume,
):
    power = clean_air_delivery_rate / room_volume
    return last_value * (decay) ** power
```

```python
power_df['power'].plot.hist()
```

```python
0.01 ** 0.001
```

    0.995405417351527

```python
def join_two_df(
    column_1,
    column_1_values,
    column_2,
    column_2_values,
    column_outcome,
    func
):

    df_1 = pd.DataFrame(
        {
            column_1: column_1_values,
            'join_col': 1
        }
    )

    df_2 = pd.DataFrame(
        {
            column_2: column_2_values,
            'join_col': 1
        }
    )

    merged = df_1.merge(df_2, on='join_col')

    merged[column_outcome] = func(merged)
    merged['value'] = 1.0

    return merged.drop(columns=['join_col'])
```

```python
decay_power_multiplier_df = join_two_df(
    column_1='decay',
    column_1_values=np.arange(0, 1, 0.01),
    column_2='power',
    column_2_values=np.arange(0, 0.02, 0.001),
    column_outcome='decay_power_multiplier',
    func=lambda df: (df['decay'] ** df['power']).round(3)
)

decay_power_multiplier_df['decay'] = decay_power_multiplier_df['decay'].round(2)
decay_power_multiplier_df['power'] = decay_power_multiplier_df['power'].round(3)
```

```python
decay_power_multiplier_df[decay_power_multiplier_df['decay'] == 0.50]
```

      decay
      power
      decay_power_multiplier
      value

      1000
      0.5
      0.000
      1.000
      1.0

      1001
      0.5
      0.001
      0.999
      1.0

      1002
      0.5
      0.002
      0.999
      1.0

      1003
      0.5
      0.003
      0.998
      1.0

      1004
      0.5
      0.004
      0.997
      1.0

      1005
      0.5
      0.005
      0.997
      1.0

      1006
      0.5
      0.006
      0.996
      1.0

      1007
      0.5
      0.007
      0.995
      1.0

      1008
      0.5
      0.008
      0.994
      1.0

      1009
      0.5
      0.009
      0.994
      1.0

      1010
      0.5
      0.010
      0.993
      1.0

      1011
      0.5
      0.011
      0.992
      1.0

      1012
      0.5
      0.012
      0.992
      1.0

      1013
      0.5
      0.013
      0.991
      1.0

      1014
      0.5
      0.014
      0.990
      1.0

      1015
      0.5
      0.015
      0.990
      1.0

      1016
      0.5
      0.016
      0.989
      1.0

      1017
      0.5
      0.017
      0.988
      1.0

      1018
      0.5
      0.018
      0.988
      1.0

      1019
      0.5
      0.019
      0.987
      1.0

```python
actual_pm_df = join_two_df(
    column_1='decay_power_multiplier',
    column_1_values=np.arange(0, 1, 0.001),
    column_2='actual_pm_t-1',
    column_2_values=list(range(0, 101)),
    column_outcome='actual_pm_t',
    func=lambda df: (df['actual_pm_t-1'] * df['decay_power_multiplier']).round().astype('int16')
)
```

```python
actual_pm_df
```

      decay_power_multiplier
      actual_pm_t-1
      actual_pm_t
      value

      0
      0.000
      0
      0
      1.0

      1
      0.000
      1
      0
      1.0

      2
      0.000
      2
      0
      1.0

      3
      0.000
      3
      0
      1.0

      4
      0.000
      4
      0
      1.0

      ...
      ...
      ...
      ...
      ...

      100995
      0.999
      96
      96
      1.0

      100996
      0.999
      97
      97
      1.0

      100997
      0.999
      98
      98
      1.0

      100998
      0.999
      99
      99
      1.0

      100999
      0.999
      100
      100
      1.0

101000 rows × 4 columns

```python
actual_pm_df['decay_power_multiplier'] = actual_pm_df['decay_power_multiplier'].round(3)
```

```python
def generate_flat_prior(prior_range, prior_name):
    df = pd.DataFrame(
        {
            prior_name: prior_range
        }
    )

    df['value'] = 1

    df['value'] = df['value'] / df['value'].sum()

    return df
```

```python
CPT(
    ParquetData(
        generate_flat_prior(
            range(0,100),
            prior_name='actual_pm t=0, CR=microfiber'
        ),
        storage_folder=storage_path
    ),
    outcomes=['actual_pm t=0, CR=microfiber']
)
```

    ConditionalProbabilityTable(
    	givens: [],
    	outcomes: ['actual_pm t=0, CR=microfiber']
    	df:

        actual_pm t=0, CR=microfiber  value
    0                              0   0.01
    1                              1   0.01
    2                              2   0.01
    3                              3   0.01
    4                              4   0.01
    ..                           ...    ...
    95                            95   0.01
    96                            96   0.01
    97                            97   0.01
    98                            98   0.01
    99                            99   0.01

    [100 rows x 2 columns])

```python
actual_pm_df
```

      decay_power_multiplier
      actual_pm_t-1
      actual_pm_t
      value

      0
      0.000
      0
      0
      1.0

      1
      0.000
      1
      0
      1.0

      2
      0.000
      2
      0
      1.0

      3
      0.000
      3
      0
      1.0

      4
      0.000
      4
      0
      1.0

      ...
      ...
      ...
      ...
      ...

      100995
      0.999
      96
      96
      1.0

      100996
      0.999
      97
      97
      1.0

      100997
      0.999
      98
      98
      1.0

      100998
      0.999
      99
      99
      1.0

      100999
      0.999
      100
      100
      1.0

101000 rows × 4 columns

```python
decay_power_multiplier_df
```

      decay
      power
      decay_power_multiplier
      value

      0
      0.00
      0.000
      1.0
      1.0

      1
      0.00
      0.001
      0.0
      1.0

      2
      0.00
      0.002
      0.0
      1.0

      3
      0.00
      0.003
      0.0
      1.0

      4
      0.00
      0.004
      0.0
      1.0

      ...
      ...
      ...
      ...
      ...

      1995
      0.99
      0.015
      1.0
      1.0

      1996
      0.99
      0.016
      1.0
      1.0

      1997
      0.99
      0.017
      1.0
      1.0

      1998
      0.99
      0.018
      1.0
      1.0

      1999
      0.99
      0.019
      1.0
      1.0

2000 rows × 4 columns

```python
decay_df = generate_flat_prior(
    np.arange(0,1, 0.01),
    prior_name='decay'
)

decay_df['decay'] = decay_df['decay'].round(2)
```

```python
power_df
```

      cadr_cfps
      room_vol
      power
      value

      0
      0.0
      690
      0.000
      1.0

      1
      0.0
      691
      0.000
      1.0

      2
      0.0
      692
      0.000
      1.0

      3
      0.0
      693
      0.000
      1.0

      4
      0.0
      694
      0.000
      1.0

      ...
      ...
      ...
      ...
      ...

      4315
      11.9
      721
      0.017
      1.0

      4316
      11.9
      722
      0.016
      1.0

      4317
      11.9
      723
      0.016
      1.0

      4318
      11.9
      724
      0.016
      1.0

      4319
      11.9
      725
      0.016
      1.0

4320 rows × 4 columns

```python
cadr_df
```

      adr_cfps
      filtration_eff
      cadr_cfps
      value

      0
      0.0
      0.00
      0.0
      1.0

      1
      0.0
      0.01
      0.0
      1.0

      2
      0.0
      0.02
      0.0
      1.0

      3
      0.0
      0.03
      0.0
      1.0

      4
      0.0
      0.04
      0.0
      1.0

      ...
      ...
      ...
      ...
      ...

      11995
      11.9
      0.95
      11.3
      1.0

      11996
      11.9
      0.96
      11.4
      1.0

      11997
      11.9
      0.97
      11.5
      1.0

      11998
      11.9
      0.98
      11.7
      1.0

      11999
      11.9
      0.99
      11.8
      1.0

12000 rows × 4 columns

```python
filtration_eff_df = generate_flat_prior(
    np.arange(0,1, 0.01),
    prior_name='filtration_eff'
)

filtration_eff_df['filtration_eff'] = filtration_eff_df['filtration_eff'].round(2)
```

```python
adr_df = generate_flat_prior(
    np.arange(0,12, 0.1),
    prior_name='adr_cfps'
)

adr_df['adr_cfps'] = adr_df['adr_cfps'].round(1)
```

```python
adr_df
```

      adr_cfps
      value

      0
      0.0
      0.008333

      1
      0.1
      0.008333

      2
      0.2
      0.008333

      3
      0.3
      0.008333

      4
      0.4
      0.008333

      ...
      ...
      ...

      115
      11.5
      0.008333

      116
      11.6
      0.008333

      117
      11.7
      0.008333

      118
      11.8
      0.008333

      119
      11.9
      0.008333

120 rows × 2 columns

```python
decay_power_multiplier_df
```

      decay
      power
      decay_power_multiplier
      value

      0
      0.00
      0.000
      1.0
      1.0

      1
      0.00
      0.001
      0.0
      1.0

      2
      0.00
      0.002
      0.0
      1.0

      3
      0.00
      0.003
      0.0
      1.0

      4
      0.00
      0.004
      0.0
      1.0

      ...
      ...
      ...
      ...
      ...

      1995
      0.99
      0.015
      1.0
      1.0

      1996
      0.99
      0.016
      1.0
      1.0

      1997
      0.99
      0.017
      1.0
      1.0

      1998
      0.99
      0.018
      1.0
      1.0

      1999
      0.99
      0.019
      1.0
      1.0

2000 rows × 4 columns

```python
power_df
```

      cadr_cfps
      room_vol
      power
      value

      0
      0.0
      690
      0.000
      1.0

      1
      0.0
      691
      0.000
      1.0

      2
      0.0
      692
      0.000
      1.0

      3
      0.0
      693
      0.000
      1.0

      4
      0.0
      694
      0.000
      1.0

      ...
      ...
      ...
      ...
      ...

      4315
      11.9
      721
      0.017
      1.0

      4316
      11.9
      722
      0.016
      1.0

      4317
      11.9
      723
      0.016
      1.0

      4318
      11.9
      724
      0.016
      1.0

      4319
      11.9
      725
      0.016
      1.0

4320 rows × 4 columns

```python
filtration_eff_df
```

      filtration_eff
      value

      0
      0.00
      0.01

      1
      0.01
      0.01

      2
      0.02
      0.01

      3
      0.03
      0.01

      4
      0.04
      0.01

      ...
      ...
      ...

      95
      0.95
      0.01

      96
      0.96
      0.01

      97
      0.97
      0.01

      98
      0.98
      0.01

      99
      0.99
      0.01

100 rows × 2 columns

```python
adr_microfiber_df = pd.DataFrame({
    'adr_cfps_microfiber': adr_microfiber.round(1),
    'value': 0
})
```

```python
normalize(adr_microfiber_df, x='adr_cfps_microfiber')
```

      value

      adr_cfps_microfiber

      3.8
      0.0118

      3.9
      0.7795

      4.0
      0.2087

```python
cadr_df
```

      adr_cfps
      filtration_eff
      cadr_cfps
      value

      0
      0.0
      0.00
      0.0
      1.0

      1
      0.0
      0.01
      0.0
      1.0

      2
      0.0
      0.02
      0.0
      1.0

      3
      0.0
      0.03
      0.0
      1.0

      4
      0.0
      0.04
      0.0
      1.0

      ...
      ...
      ...
      ...
      ...

      11995
      11.9
      0.95
      11.3
      1.0

      11996
      11.9
      0.96
      11.4
      1.0

      11997
      11.9
      0.97
      11.5
      1.0

      11998
      11.9
      0.98
      11.7
      1.0

      11999
      11.9
      0.99
      11.8
      1.0

12000 rows × 4 columns

```python
def generate_measured_pm():
    collection = []

    for actual_pm in range(120):
        samples = np.random.normal(actual_pm, 4, size=10000)
        rounded = samples.round()
        df = pd.DataFrame(
            {
                'measured_pm': rounded,
                'actual_pm': actual_pm
            }
        )

        df['measured_pm'] = df['measured_pm'].astype('Int16')

        non_neg = df[df['measured_pm'] >= 0].copy()
        non_neg.loc[:, 'value'] = 0

        #import pdb; pdb.set_trace()
        counts = non_neg.groupby(['measured_pm', 'actual_pm']).count()[['value']] / \
            non_neg.groupby(['actual_pm']).count()[['value']]

        collection.append(
            counts
        )

    counts = pd.concat(collection).reset_index()

    #import pdb; pdb.set_trace()

    return counts
```

```python
measured_pm_df = generate_measured_pm()
```

```python
measured_pm_df
```

      measured_pm
      actual_pm
      value

      0
      0
      0
      0.187219

      1
      1
      0
      0.176418

      2
      2
      0
      0.150315

      3
      3
      0
      0.133393

      4
      4
      0
      0.104770

      ...
      ...
      ...
      ...

      3554
      130
      119
      0.002100

      3555
      131
      119
      0.000900

      3556
      132
      119
      0.000400

      3557
      133
      119
      0.000200

      3558
      134
      119
      0.000200

3559 rows × 3 columns

```python
CPT(
    ParquetData(
        pd.DataFrame([
            {
                'filtration_eff AP-1512HH': 0.99,
                'value': 1.0
            }
        ]),
        storage_folder=storage_path
    ),
    outcomes=['filtration_eff AP-1512HH']
)
```

    ConditionalProbabilityTable(
    	givens: [],
    	outcomes: ['filtration_eff AP-1512HH']
    	df:

       filtration_eff AP-1512HH  value
    0                      0.99    1.0)

```python
normalize(adr_microfiber_df, x='adr_cfps_microfiber').reset_index()
```

      adr_cfps_microfiber
      value

      0
      3.8
      0.0118

      1
      3.9
      0.7795

      2
      4.0
      0.2087

```python
coway_cfps_norm = coway_cfps_normalized.reset_index()
```

```python
coway_cfps_norm.loc[: ,'AP-1512HH cfps'] = coway_cfps_norm['AP-1512HH cfps'].round(2)
```

```python
coway_cfps_norm.groupby('AP-1512HH cfps').sum().reset_index()
```

      AP-1512HH cfps
      value

      0
      3.88
      0.0004

      1
      3.89
      0.1335

      2
      3.90
      0.8408

      3
      3.91
      0.0253

```python
coway_cfps_norm.groupby('AP-1512HH cfps').sum().reset_index().rename(
                    columns={'AP-1512HH cfps': 'cadr_cfps AP-1512HH'}
                )
```

      cadr_cfps AP-1512HH
      value

      0
      3.88
      0.0004

      1
      3.89
      0.1335

      2
      3.90
      0.8408

      3
      3.91
      0.0253

```python
def generate_evidence(one_reading_per_second, cleaner_name='AP-1512HH', row_col='pm1_0'):
    return [
        {
            f'actual_pm t={i}, {cleaner_name}': row[row_col]
        }
        for i, row in one_reading_per_second.iterrows()
    ]
```

```python

```

    [{'actual_pm t=0, CR=microfiber': 40},
     {'actual_pm t=1, CR=microfiber': 40},
     {'actual_pm t=2, CR=microfiber': 40},
     {'actual_pm t=3, CR=microfiber': 40},
     {'actual_pm t=4, CR=microfiber': 41},
     {'actual_pm t=5, CR=microfiber': 40},
     {'actual_pm t=6, CR=microfiber': 40},
     {'actual_pm t=7, CR=microfiber': 41},
     {'actual_pm t=8, CR=microfiber': 40},
     {'actual_pm t=9, CR=microfiber': 40},
     {'actual_pm t=10, CR=microfiber': 40},
     {'actual_pm t=11, CR=microfiber': 38},
     {'actual_pm t=12, CR=microfiber': 38},
     {'actual_pm t=13, CR=microfiber': 38},
     {'actual_pm t=14, CR=microfiber': 38},
     {'actual_pm t=15, CR=microfiber': 38},
     {'actual_pm t=16, CR=microfiber': 38},
     {'actual_pm t=17, CR=microfiber': 38},
     {'actual_pm t=18, CR=microfiber': 39},
     {'actual_pm t=19, CR=microfiber': 39},
     {'actual_pm t=20, CR=microfiber': 39},
     {'actual_pm t=21, CR=microfiber': 39},
     {'actual_pm t=22, CR=microfiber': 40},
     {'actual_pm t=23, CR=microfiber': 40},
     {'actual_pm t=24, CR=microfiber': 40},
     {'actual_pm t=25, CR=microfiber': 39},
     {'actual_pm t=26, CR=microfiber': 39},
     {'actual_pm t=27, CR=microfiber': 39},
     {'actual_pm t=28, CR=microfiber': 39},
     {'actual_pm t=29, CR=microfiber': 39},
     {'actual_pm t=30, CR=microfiber': 38},
     {'actual_pm t=31, CR=microfiber': 37},
     {'actual_pm t=32, CR=microfiber': 37},
     {'actual_pm t=33, CR=microfiber': 37},
     {'actual_pm t=34, CR=microfiber': 38},
     {'actual_pm t=35, CR=microfiber': 38},
     {'actual_pm t=36, CR=microfiber': 38},
     {'actual_pm t=37, CR=microfiber': 38},
     {'actual_pm t=38, CR=microfiber': 40},
     {'actual_pm t=39, CR=microfiber': 40},
     {'actual_pm t=40, CR=microfiber': 41},
     {'actual_pm t=41, CR=microfiber': 41},
     {'actual_pm t=42, CR=microfiber': 41},
     {'actual_pm t=43, CR=microfiber': 41},
     {'actual_pm t=44, CR=microfiber': 40},
     {'actual_pm t=45, CR=microfiber': 40},
     {'actual_pm t=46, CR=microfiber': 40},
     {'actual_pm t=47, CR=microfiber': 40},
     {'actual_pm t=48, CR=microfiber': 40},
     {'actual_pm t=49, CR=microfiber': 40},
     {'actual_pm t=50, CR=microfiber': 40},
     {'actual_pm t=51, CR=microfiber': 40},
     {'actual_pm t=52, CR=microfiber': 38},
     {'actual_pm t=53, CR=microfiber': 38},
     {'actual_pm t=54, CR=microfiber': 38},
     {'actual_pm t=55, CR=microfiber': 38},
     {'actual_pm t=56, CR=microfiber': 38},
     {'actual_pm t=57, CR=microfiber': 38},
     {'actual_pm t=58, CR=microfiber': 38},
     {'actual_pm t=59, CR=microfiber': 38},
     {'actual_pm t=60, CR=microfiber': 38},
     {'actual_pm t=61, CR=microfiber': 38},
     {'actual_pm t=62, CR=microfiber': 38},
     {'actual_pm t=63, CR=microfiber': 38},
     {'actual_pm t=64, CR=microfiber': 38},
     {'actual_pm t=65, CR=microfiber': 38},
     {'actual_pm t=66, CR=microfiber': 38},
     {'actual_pm t=67, CR=microfiber': 38},
     {'actual_pm t=68, CR=microfiber': 38},
     {'actual_pm t=69, CR=microfiber': 38},
     {'actual_pm t=70, CR=microfiber': 36},
     {'actual_pm t=71, CR=microfiber': 36},
     {'actual_pm t=72, CR=microfiber': 38},
     {'actual_pm t=73, CR=microfiber': 38},
     {'actual_pm t=74, CR=microfiber': 37},
     {'actual_pm t=75, CR=microfiber': 37},
     {'actual_pm t=76, CR=microfiber': 37},
     {'actual_pm t=77, CR=microfiber': 37},
     {'actual_pm t=78, CR=microfiber': 37},
     {'actual_pm t=79, CR=microfiber': 38},
     {'actual_pm t=80, CR=microfiber': 38},
     {'actual_pm t=81, CR=microfiber': 38},
     {'actual_pm t=82, CR=microfiber': 37},
     {'actual_pm t=83, CR=microfiber': 37},
     {'actual_pm t=84, CR=microfiber': 37},
     {'actual_pm t=85, CR=microfiber': 37},
     {'actual_pm t=86, CR=microfiber': 37},
     {'actual_pm t=87, CR=microfiber': 39},
     {'actual_pm t=88, CR=microfiber': 39},
     {'actual_pm t=89, CR=microfiber': 39},
     {'actual_pm t=90, CR=microfiber': 39},
     {'actual_pm t=91, CR=microfiber': 39},
     {'actual_pm t=92, CR=microfiber': 39},
     {'actual_pm t=93, CR=microfiber': 39},
     {'actual_pm t=94, CR=microfiber': 39},
     {'actual_pm t=95, CR=microfiber': 38},
     {'actual_pm t=96, CR=microfiber': 38},
     {'actual_pm t=97, CR=microfiber': 38},
     {'actual_pm t=98, CR=microfiber': 37},
     {'actual_pm t=99, CR=microfiber': 38},
     {'actual_pm t=100, CR=microfiber': 38},
     {'actual_pm t=101, CR=microfiber': 38},
     {'actual_pm t=102, CR=microfiber': 37},
     {'actual_pm t=103, CR=microfiber': 37},
     {'actual_pm t=104, CR=microfiber': 37},
     {'actual_pm t=105, CR=microfiber': 36},
     {'actual_pm t=106, CR=microfiber': 37},
     {'actual_pm t=107, CR=microfiber': 37},
     {'actual_pm t=108, CR=microfiber': 37},
     {'actual_pm t=109, CR=microfiber': 37},
     {'actual_pm t=110, CR=microfiber': 37},
     {'actual_pm t=111, CR=microfiber': 37},
     {'actual_pm t=112, CR=microfiber': 37},
     {'actual_pm t=113, CR=microfiber': 37},
     {'actual_pm t=114, CR=microfiber': 37},
     {'actual_pm t=115, CR=microfiber': 37},
     {'actual_pm t=116, CR=microfiber': 38},
     {'actual_pm t=117, CR=microfiber': 38},
     {'actual_pm t=118, CR=microfiber': 38},
     {'actual_pm t=119, CR=microfiber': 37},
     {'actual_pm t=120, CR=microfiber': 38},
     {'actual_pm t=121, CR=microfiber': 38},
     {'actual_pm t=122, CR=microfiber': 37},
     {'actual_pm t=123, CR=microfiber': 36},
     {'actual_pm t=124, CR=microfiber': 36},
     {'actual_pm t=125, CR=microfiber': 35},
     {'actual_pm t=126, CR=microfiber': 35},
     {'actual_pm t=127, CR=microfiber': 35},
     {'actual_pm t=128, CR=microfiber': 36},
     {'actual_pm t=129, CR=microfiber': 36},
     {'actual_pm t=130, CR=microfiber': 36},
     {'actual_pm t=131, CR=microfiber': 37},
     {'actual_pm t=132, CR=microfiber': 37},
     {'actual_pm t=133, CR=microfiber': 37},
     {'actual_pm t=134, CR=microfiber': 37},
     {'actual_pm t=135, CR=microfiber': 37},
     {'actual_pm t=136, CR=microfiber': 37},
     {'actual_pm t=137, CR=microfiber': 37},
     {'actual_pm t=138, CR=microfiber': 37},
     {'actual_pm t=139, CR=microfiber': 37},
     {'actual_pm t=140, CR=microfiber': 37},
     {'actual_pm t=141, CR=microfiber': 37},
     {'actual_pm t=142, CR=microfiber': 37},
     {'actual_pm t=143, CR=microfiber': 37},
     {'actual_pm t=144, CR=microfiber': 37},
     {'actual_pm t=145, CR=microfiber': 37},
     {'actual_pm t=146, CR=microfiber': 37},
     {'actual_pm t=147, CR=microfiber': 37},
     {'actual_pm t=148, CR=microfiber': 38},
     {'actual_pm t=149, CR=microfiber': 38},
     {'actual_pm t=150, CR=microfiber': 38},
     {'actual_pm t=151, CR=microfiber': 38},
     {'actual_pm t=152, CR=microfiber': 38},
     {'actual_pm t=153, CR=microfiber': 40},
     {'actual_pm t=154, CR=microfiber': 40},
     {'actual_pm t=155, CR=microfiber': 39},
     {'actual_pm t=156, CR=microfiber': 39},
     {'actual_pm t=157, CR=microfiber': 39},
     {'actual_pm t=158, CR=microfiber': 37},
     {'actual_pm t=159, CR=microfiber': 37},
     {'actual_pm t=160, CR=microfiber': 37},
     {'actual_pm t=161, CR=microfiber': 37},
     {'actual_pm t=162, CR=microfiber': 37},
     {'actual_pm t=163, CR=microfiber': 37},
     {'actual_pm t=164, CR=microfiber': 37},
     {'actual_pm t=165, CR=microfiber': 37},
     {'actual_pm t=166, CR=microfiber': 37},
     {'actual_pm t=167, CR=microfiber': 37},
     {'actual_pm t=168, CR=microfiber': 37},
     {'actual_pm t=169, CR=microfiber': 37},
     {'actual_pm t=170, CR=microfiber': 37},
     {'actual_pm t=171, CR=microfiber': 37},
     {'actual_pm t=172, CR=microfiber': 37},
     {'actual_pm t=173, CR=microfiber': 37},
     {'actual_pm t=174, CR=microfiber': 37},
     {'actual_pm t=175, CR=microfiber': 37},
     {'actual_pm t=176, CR=microfiber': 38},
     {'actual_pm t=177, CR=microfiber': 38},
     {'actual_pm t=178, CR=microfiber': 38},
     {'actual_pm t=179, CR=microfiber': 37},
     {'actual_pm t=180, CR=microfiber': 37},
     {'actual_pm t=181, CR=microfiber': 37},
     {'actual_pm t=182, CR=microfiber': 38},
     {'actual_pm t=183, CR=microfiber': 38},
     {'actual_pm t=184, CR=microfiber': 38},
     {'actual_pm t=185, CR=microfiber': 39},
     {'actual_pm t=186, CR=microfiber': 37},
     {'actual_pm t=187, CR=microfiber': 37},
     {'actual_pm t=188, CR=microfiber': 37},
     {'actual_pm t=189, CR=microfiber': 37},
     {'actual_pm t=190, CR=microfiber': 37},
     {'actual_pm t=191, CR=microfiber': 37},
     {'actual_pm t=192, CR=microfiber': 36},
     {'actual_pm t=193, CR=microfiber': 36},
     {'actual_pm t=194, CR=microfiber': 36},
     {'actual_pm t=195, CR=microfiber': 36},
     {'actual_pm t=196, CR=microfiber': 36},
     {'actual_pm t=197, CR=microfiber': 36},
     {'actual_pm t=198, CR=microfiber': 36},
     {'actual_pm t=199, CR=microfiber': 36},
     {'actual_pm t=200, CR=microfiber': 36},
     {'actual_pm t=201, CR=microfiber': 36},
     {'actual_pm t=202, CR=microfiber': 36},
     {'actual_pm t=203, CR=microfiber': 37},
     {'actual_pm t=204, CR=microfiber': 37},
     {'actual_pm t=205, CR=microfiber': 37},
     {'actual_pm t=206, CR=microfiber': 37},
     {'actual_pm t=207, CR=microfiber': 37},
     {'actual_pm t=208, CR=microfiber': 37},
     {'actual_pm t=209, CR=microfiber': 37},
     {'actual_pm t=210, CR=microfiber': 37},
     {'actual_pm t=211, CR=microfiber': 36},
     {'actual_pm t=212, CR=microfiber': 36},
     {'actual_pm t=213, CR=microfiber': 36},
     {'actual_pm t=214, CR=microfiber': 36},
     {'actual_pm t=215, CR=microfiber': 36},
     {'actual_pm t=216, CR=microfiber': 36},
     {'actual_pm t=217, CR=microfiber': 36},
     {'actual_pm t=218, CR=microfiber': 36},
     {'actual_pm t=219, CR=microfiber': 36},
     {'actual_pm t=220, CR=microfiber': 36},
     {'actual_pm t=221, CR=microfiber': 37},
     {'actual_pm t=222, CR=microfiber': 37},
     {'actual_pm t=223, CR=microfiber': 37},
     {'actual_pm t=224, CR=microfiber': 37},
     {'actual_pm t=225, CR=microfiber': 37},
     {'actual_pm t=226, CR=microfiber': 36},
     {'actual_pm t=227, CR=microfiber': 36},
     {'actual_pm t=228, CR=microfiber': 36},
     {'actual_pm t=229, CR=microfiber': 37},
     {'actual_pm t=230, CR=microfiber': 36},
     {'actual_pm t=231, CR=microfiber': 36},
     {'actual_pm t=232, CR=microfiber': 36},
     {'actual_pm t=233, CR=microfiber': 36},
     {'actual_pm t=234, CR=microfiber': 36},
     {'actual_pm t=235, CR=microfiber': 36},
     {'actual_pm t=236, CR=microfiber': 36},
     {'actual_pm t=237, CR=microfiber': 36},
     {'actual_pm t=238, CR=microfiber': 36},
     {'actual_pm t=239, CR=microfiber': 36},
     {'actual_pm t=240, CR=microfiber': 36},
     {'actual_pm t=241, CR=microfiber': 36},
     {'actual_pm t=242, CR=microfiber': 36},
     {'actual_pm t=243, CR=microfiber': 36},
     {'actual_pm t=244, CR=microfiber': 37},
     {'actual_pm t=245, CR=microfiber': 37},
     {'actual_pm t=246, CR=microfiber': 37},
     {'actual_pm t=247, CR=microfiber': 36},
     {'actual_pm t=248, CR=microfiber': 36},
     {'actual_pm t=249, CR=microfiber': 37},
     {'actual_pm t=250, CR=microfiber': 36},
     {'actual_pm t=251, CR=microfiber': 36},
     {'actual_pm t=252, CR=microfiber': 37},
     {'actual_pm t=253, CR=microfiber': 36},
     {'actual_pm t=254, CR=microfiber': 37},
     {'actual_pm t=255, CR=microfiber': 37},
     {'actual_pm t=256, CR=microfiber': 37},
     {'actual_pm t=257, CR=microfiber': 37},
     {'actual_pm t=258, CR=microfiber': 37},
     {'actual_pm t=259, CR=microfiber': 37},
     {'actual_pm t=260, CR=microfiber': 37},
     {'actual_pm t=261, CR=microfiber': 37},
     {'actual_pm t=262, CR=microfiber': 37},
     {'actual_pm t=263, CR=microfiber': 37},
     {'actual_pm t=264, CR=microfiber': 37},
     {'actual_pm t=265, CR=microfiber': 37},
     {'actual_pm t=266, CR=microfiber': 37},
     {'actual_pm t=267, CR=microfiber': 36},
     {'actual_pm t=268, CR=microfiber': 36},
     {'actual_pm t=269, CR=microfiber': 36},
     {'actual_pm t=270, CR=microfiber': 36},
     {'actual_pm t=271, CR=microfiber': 36},
     {'actual_pm t=272, CR=microfiber': 36},
     {'actual_pm t=273, CR=microfiber': 36},
     {'actual_pm t=274, CR=microfiber': 36},
     {'actual_pm t=275, CR=microfiber': 36},
     {'actual_pm t=276, CR=microfiber': 36},
     {'actual_pm t=277, CR=microfiber': 35},
     {'actual_pm t=278, CR=microfiber': 35},
     {'actual_pm t=279, CR=microfiber': 35},
     {'actual_pm t=280, CR=microfiber': 35},
     {'actual_pm t=281, CR=microfiber': 36},
     {'actual_pm t=282, CR=microfiber': 35},
     {'actual_pm t=283, CR=microfiber': 35},
     {'actual_pm t=284, CR=microfiber': 36},
     {'actual_pm t=285, CR=microfiber': 37},
     {'actual_pm t=286, CR=microfiber': 37},
     {'actual_pm t=287, CR=microfiber': 37},
     {'actual_pm t=288, CR=microfiber': 37},
     {'actual_pm t=289, CR=microfiber': 37},
     {'actual_pm t=290, CR=microfiber': 37},
     {'actual_pm t=291, CR=microfiber': 37},
     {'actual_pm t=292, CR=microfiber': 37},
     {'actual_pm t=293, CR=microfiber': 37},
     {'actual_pm t=294, CR=microfiber': 36},
     {'actual_pm t=295, CR=microfiber': 36},
     {'actual_pm t=296, CR=microfiber': 36},
     {'actual_pm t=297, CR=microfiber': 36},
     {'actual_pm t=298, CR=microfiber': 36},
     {'actual_pm t=299, CR=microfiber': 37},
     {'actual_pm t=300, CR=microfiber': 37},
     {'actual_pm t=301, CR=microfiber': 37},
     {'actual_pm t=302, CR=microfiber': 37},
     {'actual_pm t=303, CR=microfiber': 37},
     {'actual_pm t=304, CR=microfiber': 36},
     {'actual_pm t=305, CR=microfiber': 36},
     {'actual_pm t=306, CR=microfiber': 36},
     {'actual_pm t=307, CR=microfiber': 36},
     {'actual_pm t=308, CR=microfiber': 35},
     {'actual_pm t=309, CR=microfiber': 36},
     {'actual_pm t=310, CR=microfiber': 35},
     {'actual_pm t=311, CR=microfiber': 35},
     {'actual_pm t=312, CR=microfiber': 35},
     {'actual_pm t=313, CR=microfiber': 35},
     {'actual_pm t=314, CR=microfiber': 36},
     {'actual_pm t=315, CR=microfiber': 36},
     {'actual_pm t=316, CR=microfiber': 36},
     {'actual_pm t=317, CR=microfiber': 37},
     {'actual_pm t=318, CR=microfiber': 37},
     {'actual_pm t=319, CR=microfiber': 36},
     {'actual_pm t=320, CR=microfiber': 36},
     {'actual_pm t=321, CR=microfiber': 35},
     {'actual_pm t=322, CR=microfiber': 36},
     {'actual_pm t=323, CR=microfiber': 35},
     {'actual_pm t=324, CR=microfiber': 35},
     {'actual_pm t=325, CR=microfiber': 35},
     {'actual_pm t=326, CR=microfiber': 33},
     {'actual_pm t=327, CR=microfiber': 32},
     {'actual_pm t=328, CR=microfiber': 33},
     {'actual_pm t=329, CR=microfiber': 33},
     {'actual_pm t=330, CR=microfiber': 33},
     {'actual_pm t=331, CR=microfiber': 33},
     {'actual_pm t=332, CR=microfiber': 35},
     {'actual_pm t=333, CR=microfiber': 35},
     {'actual_pm t=334, CR=microfiber': 34},
     {'actual_pm t=335, CR=microfiber': 34},
     {'actual_pm t=336, CR=microfiber': 34},
     {'actual_pm t=337, CR=microfiber': 34},
     {'actual_pm t=338, CR=microfiber': 34},
     {'actual_pm t=339, CR=microfiber': 34},
     {'actual_pm t=340, CR=microfiber': 36},
     {'actual_pm t=341, CR=microfiber': 36},
     {'actual_pm t=342, CR=microfiber': 36},
     {'actual_pm t=343, CR=microfiber': 37},
     {'actual_pm t=344, CR=microfiber': 37},
     {'actual_pm t=345, CR=microfiber': 36},
     {'actual_pm t=346, CR=microfiber': 36},
     {'actual_pm t=347, CR=microfiber': 36},
     {'actual_pm t=348, CR=microfiber': 36},
     {'actual_pm t=349, CR=microfiber': 36},
     {'actual_pm t=350, CR=microfiber': 35},
     {'actual_pm t=351, CR=microfiber': 36},
     {'actual_pm t=352, CR=microfiber': 36},
     {'actual_pm t=353, CR=microfiber': 36},
     {'actual_pm t=354, CR=microfiber': 36},
     {'actual_pm t=355, CR=microfiber': 36},
     {'actual_pm t=356, CR=microfiber': 36},
     {'actual_pm t=357, CR=microfiber': 36},
     {'actual_pm t=358, CR=microfiber': 35},
     {'actual_pm t=359, CR=microfiber': 36},
     {'actual_pm t=360, CR=microfiber': 37},
     {'actual_pm t=361, CR=microfiber': 36},
     {'actual_pm t=362, CR=microfiber': 36},
     {'actual_pm t=363, CR=microfiber': 36},
     {'actual_pm t=364, CR=microfiber': 36},
     {'actual_pm t=365, CR=microfiber': 36},
     {'actual_pm t=366, CR=microfiber': 36},
     {'actual_pm t=367, CR=microfiber': 36},
     {'actual_pm t=368, CR=microfiber': 36},
     {'actual_pm t=369, CR=microfiber': 36},
     {'actual_pm t=370, CR=microfiber': 36},
     {'actual_pm t=371, CR=microfiber': 34},
     {'actual_pm t=372, CR=microfiber': 34},
     {'actual_pm t=373, CR=microfiber': 36},
     {'actual_pm t=374, CR=microfiber': 36},
     {'actual_pm t=375, CR=microfiber': 36},
     {'actual_pm t=376, CR=microfiber': 36},
     {'actual_pm t=377, CR=microfiber': 36},
     {'actual_pm t=378, CR=microfiber': 36},
     {'actual_pm t=379, CR=microfiber': 36},
     {'actual_pm t=380, CR=microfiber': 36},
     {'actual_pm t=381, CR=microfiber': 36},
     {'actual_pm t=382, CR=microfiber': 36},
     {'actual_pm t=383, CR=microfiber': 36},
     {'actual_pm t=384, CR=microfiber': 36},
     {'actual_pm t=385, CR=microfiber': 36},
     {'actual_pm t=386, CR=microfiber': 36},
     {'actual_pm t=387, CR=microfiber': 36},
     {'actual_pm t=388, CR=microfiber': 36},
     {'actual_pm t=389, CR=microfiber': 36},
     {'actual_pm t=390, CR=microfiber': 36},
     {'actual_pm t=391, CR=microfiber': 36},
     {'actual_pm t=392, CR=microfiber': 36},
     {'actual_pm t=393, CR=microfiber': 35},
     {'actual_pm t=394, CR=microfiber': 35},
     {'actual_pm t=395, CR=microfiber': 35},
     {'actual_pm t=396, CR=microfiber': 35},
     {'actual_pm t=397, CR=microfiber': 34},
     {'actual_pm t=398, CR=microfiber': 34},
     {'actual_pm t=399, CR=microfiber': 34},
     {'actual_pm t=400, CR=microfiber': 34},
     {'actual_pm t=401, CR=microfiber': 34},
     {'actual_pm t=402, CR=microfiber': 34},
     {'actual_pm t=403, CR=microfiber': 33},
     {'actual_pm t=404, CR=microfiber': 32},
     {'actual_pm t=405, CR=microfiber': 33},
     {'actual_pm t=406, CR=microfiber': 35},
     {'actual_pm t=407, CR=microfiber': 35},
     {'actual_pm t=408, CR=microfiber': 35},
     {'actual_pm t=409, CR=microfiber': 35},
     {'actual_pm t=410, CR=microfiber': 36},
     {'actual_pm t=411, CR=microfiber': 35},
     {'actual_pm t=412, CR=microfiber': 35},
     {'actual_pm t=413, CR=microfiber': 36},
     {'actual_pm t=414, CR=microfiber': 36},
     {'actual_pm t=415, CR=microfiber': 36},
     {'actual_pm t=416, CR=microfiber': 35},
     {'actual_pm t=417, CR=microfiber': 35},
     {'actual_pm t=418, CR=microfiber': 35},
     {'actual_pm t=419, CR=microfiber': 35},
     {'actual_pm t=420, CR=microfiber': 35},
     {'actual_pm t=421, CR=microfiber': 35},
     {'actual_pm t=422, CR=microfiber': 36},
     {'actual_pm t=423, CR=microfiber': 36},
     {'actual_pm t=424, CR=microfiber': 36},
     {'actual_pm t=425, CR=microfiber': 35},
     {'actual_pm t=426, CR=microfiber': 34},
     {'actual_pm t=427, CR=microfiber': 34},
     {'actual_pm t=428, CR=microfiber': 34},
     {'actual_pm t=429, CR=microfiber': 34},
     {'actual_pm t=430, CR=microfiber': 35},
     {'actual_pm t=431, CR=microfiber': 35},
     {'actual_pm t=432, CR=microfiber': 35},
     {'actual_pm t=433, CR=microfiber': 34},
     {'actual_pm t=434, CR=microfiber': 34},
     {'actual_pm t=435, CR=microfiber': 34},
     {'actual_pm t=436, CR=microfiber': 35},
     {'actual_pm t=437, CR=microfiber': 35},
     {'actual_pm t=438, CR=microfiber': 35},
     {'actual_pm t=439, CR=microfiber': 35},
     {'actual_pm t=440, CR=microfiber': 35},
     {'actual_pm t=441, CR=microfiber': 34},
     {'actual_pm t=442, CR=microfiber': 34},
     {'actual_pm t=443, CR=microfiber': 36},
     {'actual_pm t=444, CR=microfiber': 36},
     {'actual_pm t=445, CR=microfiber': 34},
     {'actual_pm t=446, CR=microfiber': 34},
     {'actual_pm t=447, CR=microfiber': 34},
     {'actual_pm t=448, CR=microfiber': 35},
     {'actual_pm t=449, CR=microfiber': 35},
     {'actual_pm t=450, CR=microfiber': 34},
     {'actual_pm t=451, CR=microfiber': 34},
     {'actual_pm t=452, CR=microfiber': 34},
     {'actual_pm t=453, CR=microfiber': 34},
     {'actual_pm t=454, CR=microfiber': 34},
     {'actual_pm t=455, CR=microfiber': 34},
     {'actual_pm t=456, CR=microfiber': 33},
     {'actual_pm t=457, CR=microfiber': 32},
     {'actual_pm t=458, CR=microfiber': 32},
     {'actual_pm t=459, CR=microfiber': 33},
     {'actual_pm t=460, CR=microfiber': 33},
     {'actual_pm t=461, CR=microfiber': 32},
     {'actual_pm t=462, CR=microfiber': 32},
     {'actual_pm t=463, CR=microfiber': 32},
     {'actual_pm t=464, CR=microfiber': 32},
     {'actual_pm t=465, CR=microfiber': 32},
     {'actual_pm t=466, CR=microfiber': 33},
     {'actual_pm t=467, CR=microfiber': 33},
     {'actual_pm t=468, CR=microfiber': 32},
     {'actual_pm t=469, CR=microfiber': 32},
     {'actual_pm t=470, CR=microfiber': 32},
     {'actual_pm t=471, CR=microfiber': 32},
     {'actual_pm t=472, CR=microfiber': 32},
     {'actual_pm t=473, CR=microfiber': 32},
     {'actual_pm t=474, CR=microfiber': 32},
     {'actual_pm t=475, CR=microfiber': 32},
     {'actual_pm t=476, CR=microfiber': 32},
     {'actual_pm t=477, CR=microfiber': 32},
     {'actual_pm t=478, CR=microfiber': 32},
     {'actual_pm t=479, CR=microfiber': 32},
     {'actual_pm t=480, CR=microfiber': 32},
     {'actual_pm t=481, CR=microfiber': 32},
     {'actual_pm t=482, CR=microfiber': 32},
     {'actual_pm t=483, CR=microfiber': 32},
     {'actual_pm t=484, CR=microfiber': 33},
     {'actual_pm t=485, CR=microfiber': 33},
     {'actual_pm t=486, CR=microfiber': 33},
     {'actual_pm t=487, CR=microfiber': 32},
     {'actual_pm t=488, CR=microfiber': 33},
     {'actual_pm t=489, CR=microfiber': 33},
     {'actual_pm t=490, CR=microfiber': 34},
     {'actual_pm t=491, CR=microfiber': 35},
     {'actual_pm t=492, CR=microfiber': 35},
     {'actual_pm t=493, CR=microfiber': 36},
     {'actual_pm t=494, CR=microfiber': 35},
     {'actual_pm t=495, CR=microfiber': 36},
     {'actual_pm t=496, CR=microfiber': 36},
     {'actual_pm t=497, CR=microfiber': 36},
     {'actual_pm t=498, CR=microfiber': 36},
     {'actual_pm t=499, CR=microfiber': 35},
     {'actual_pm t=500, CR=microfiber': 35},
     {'actual_pm t=501, CR=microfiber': 35},
     {'actual_pm t=502, CR=microfiber': 35},
     {'actual_pm t=503, CR=microfiber': 35},
     {'actual_pm t=504, CR=microfiber': 33},
     {'actual_pm t=505, CR=microfiber': 33},
     {'actual_pm t=506, CR=microfiber': 34},
     {'actual_pm t=507, CR=microfiber': 35},
     {'actual_pm t=508, CR=microfiber': 33},
     {'actual_pm t=509, CR=microfiber': 35},
     {'actual_pm t=510, CR=microfiber': 35},
     {'actual_pm t=511, CR=microfiber': 33},
     {'actual_pm t=512, CR=microfiber': 33},
     {'actual_pm t=513, CR=microfiber': 32},
     {'actual_pm t=514, CR=microfiber': 32},
     {'actual_pm t=515, CR=microfiber': 32},
     {'actual_pm t=516, CR=microfiber': 32},
     {'actual_pm t=517, CR=microfiber': 32},
     {'actual_pm t=518, CR=microfiber': 32},
     {'actual_pm t=519, CR=microfiber': 32},
     {'actual_pm t=520, CR=microfiber': 32},
     {'actual_pm t=521, CR=microfiber': 32},
     {'actual_pm t=522, CR=microfiber': 32},
     {'actual_pm t=523, CR=microfiber': 32},
     {'actual_pm t=524, CR=microfiber': 32},
     {'actual_pm t=525, CR=microfiber': 32},
     {'actual_pm t=526, CR=microfiber': 32},
     {'actual_pm t=527, CR=microfiber': 32},
     {'actual_pm t=528, CR=microfiber': 33},
     {'actual_pm t=529, CR=microfiber': 33},
     {'actual_pm t=530, CR=microfiber': 33},
     {'actual_pm t=531, CR=microfiber': 33},
     {'actual_pm t=532, CR=microfiber': 33},
     {'actual_pm t=533, CR=microfiber': 32},
     {'actual_pm t=534, CR=microfiber': 32},
     {'actual_pm t=535, CR=microfiber': 33},
     {'actual_pm t=536, CR=microfiber': 33},
     {'actual_pm t=537, CR=microfiber': 32},
     {'actual_pm t=538, CR=microfiber': 33},
     {'actual_pm t=539, CR=microfiber': 33},
     {'actual_pm t=540, CR=microfiber': 33},
     {'actual_pm t=541, CR=microfiber': 33},
     {'actual_pm t=542, CR=microfiber': 32},
     {'actual_pm t=543, CR=microfiber': 32},
     {'actual_pm t=544, CR=microfiber': 32},
     {'actual_pm t=545, CR=microfiber': 32},
     {'actual_pm t=546, CR=microfiber': 33},
     {'actual_pm t=547, CR=microfiber': 33},
     {'actual_pm t=548, CR=microfiber': 33},
     {'actual_pm t=549, CR=microfiber': 33},
     {'actual_pm t=550, CR=microfiber': 33},
     {'actual_pm t=551, CR=microfiber': 33},
     {'actual_pm t=552, CR=microfiber': 33},
     {'actual_pm t=553, CR=microfiber': 33},
     {'actual_pm t=554, CR=microfiber': 32},
     {'actual_pm t=555, CR=microfiber': 32},
     {'actual_pm t=556, CR=microfiber': 31},
     {'actual_pm t=557, CR=microfiber': 31},
     {'actual_pm t=558, CR=microfiber': 32},
     {'actual_pm t=559, CR=microfiber': 32},
     {'actual_pm t=560, CR=microfiber': 32},
     {'actual_pm t=561, CR=microfiber': 32},
     {'actual_pm t=562, CR=microfiber': 32},
     {'actual_pm t=563, CR=microfiber': 32},
     {'actual_pm t=564, CR=microfiber': 32},
     {'actual_pm t=565, CR=microfiber': 32},
     {'actual_pm t=566, CR=microfiber': 32},
     {'actual_pm t=567, CR=microfiber': 33},
     {'actual_pm t=568, CR=microfiber': 33},
     {'actual_pm t=569, CR=microfiber': 35},
     {'actual_pm t=570, CR=microfiber': 35},
     {'actual_pm t=571, CR=microfiber': 33},
     {'actual_pm t=572, CR=microfiber': 32},
     {'actual_pm t=573, CR=microfiber': 32},
     {'actual_pm t=574, CR=microfiber': 31},
     {'actual_pm t=575, CR=microfiber': 31},
     {'actual_pm t=576, CR=microfiber': 31},
     {'actual_pm t=577, CR=microfiber': 31},
     {'actual_pm t=578, CR=microfiber': 31},
     {'actual_pm t=579, CR=microfiber': 31},
     {'actual_pm t=580, CR=microfiber': 31},
     {'actual_pm t=581, CR=microfiber': 31},
     {'actual_pm t=582, CR=microfiber': 32},
     {'actual_pm t=583, CR=microfiber': 32},
     {'actual_pm t=584, CR=microfiber': 32},
     {'actual_pm t=585, CR=microfiber': 33},
     {'actual_pm t=586, CR=microfiber': 35},
     {'actual_pm t=587, CR=microfiber': 35},
     {'actual_pm t=588, CR=microfiber': 33},
     {'actual_pm t=589, CR=microfiber': 35},
     {'actual_pm t=590, CR=microfiber': 35},
     {'actual_pm t=591, CR=microfiber': 33},
     {'actual_pm t=592, CR=microfiber': 33},
     {'actual_pm t=593, CR=microfiber': 32},
     {'actual_pm t=594, CR=microfiber': 32},
     {'actual_pm t=595, CR=microfiber': 32},
     {'actual_pm t=596, CR=microfiber': 32},
     {'actual_pm t=597, CR=microfiber': 32},
     {'actual_pm t=598, CR=microfiber': 30},
     {'actual_pm t=599, CR=microfiber': 30},
     {'actual_pm t=600, CR=microfiber': 30},
     {'actual_pm t=601, CR=microfiber': 30},
     {'actual_pm t=602, CR=microfiber': 33},
     {'actual_pm t=603, CR=microfiber': 30},
     {'actual_pm t=604, CR=microfiber': 30},
     {'actual_pm t=605, CR=microfiber': 30},
     {'actual_pm t=606, CR=microfiber': 32},
     {'actual_pm t=607, CR=microfiber': 32},
     {'actual_pm t=608, CR=microfiber': 32},
     {'actual_pm t=609, CR=microfiber': 32},
     {'actual_pm t=610, CR=microfiber': 32},
     {'actual_pm t=611, CR=microfiber': 31},
     {'actual_pm t=612, CR=microfiber': 32},
     {'actual_pm t=613, CR=microfiber': 32},
     {'actual_pm t=614, CR=microfiber': 32},
     {'actual_pm t=615, CR=microfiber': 32},
     {'actual_pm t=616, CR=microfiber': 33},
     {'actual_pm t=617, CR=microfiber': 33},
     {'actual_pm t=618, CR=microfiber': 32},
     {'actual_pm t=619, CR=microfiber': 32},
     {'actual_pm t=620, CR=microfiber': 32},
     {'actual_pm t=621, CR=microfiber': 32},
     {'actual_pm t=622, CR=microfiber': 32},
     {'actual_pm t=623, CR=microfiber': 32},
     {'actual_pm t=624, CR=microfiber': 32},
     {'actual_pm t=625, CR=microfiber': 32},
     {'actual_pm t=626, CR=microfiber': 32},
     {'actual_pm t=627, CR=microfiber': 32},
     {'actual_pm t=628, CR=microfiber': 32},
     {'actual_pm t=629, CR=microfiber': 32},
     {'actual_pm t=630, CR=microfiber': 32},
     {'actual_pm t=631, CR=microfiber': 32},
     {'actual_pm t=632, CR=microfiber': 32},
     {'actual_pm t=633, CR=microfiber': 32},
     {'actual_pm t=634, CR=microfiber': 32},
     {'actual_pm t=635, CR=microfiber': 32},
     {'actual_pm t=636, CR=microfiber': 31},
     {'actual_pm t=637, CR=microfiber': 31},
     {'actual_pm t=638, CR=microfiber': 31},
     {'actual_pm t=639, CR=microfiber': 31},
     {'actual_pm t=640, CR=microfiber': 29},
     {'actual_pm t=641, CR=microfiber': 31},
     {'actual_pm t=642, CR=microfiber': 31},
     {'actual_pm t=643, CR=microfiber': 29},
     {'actual_pm t=644, CR=microfiber': 29},
     {'actual_pm t=645, CR=microfiber': 31},
     {'actual_pm t=646, CR=microfiber': 31},
     {'actual_pm t=647, CR=microfiber': 31},
     {'actual_pm t=648, CR=microfiber': 30},
     {'actual_pm t=649, CR=microfiber': 30},
     {'actual_pm t=650, CR=microfiber': 30},
     {'actual_pm t=651, CR=microfiber': 30},
     {'actual_pm t=652, CR=microfiber': 32},
     {'actual_pm t=653, CR=microfiber': 32},
     {'actual_pm t=654, CR=microfiber': 32},
     {'actual_pm t=655, CR=microfiber': 32},
     {'actual_pm t=656, CR=microfiber': 33},
     {'actual_pm t=657, CR=microfiber': 33},
     {'actual_pm t=658, CR=microfiber': 33},
     {'actual_pm t=659, CR=microfiber': 33},
     {'actual_pm t=660, CR=microfiber': 33},
     {'actual_pm t=661, CR=microfiber': 34},
     {'actual_pm t=662, CR=microfiber': 34},
     {'actual_pm t=663, CR=microfiber': 35},
     {'actual_pm t=664, CR=microfiber': 35},
     {'actual_pm t=665, CR=microfiber': 33},
     {'actual_pm t=666, CR=microfiber': 32},
     {'actual_pm t=667, CR=microfiber': 32},
     {'actual_pm t=668, CR=microfiber': 30},
     {'actual_pm t=669, CR=microfiber': 30},
     {'actual_pm t=670, CR=microfiber': 32},
     {'actual_pm t=671, CR=microfiber': 30},
     {'actual_pm t=672, CR=microfiber': 31},
     {'actual_pm t=673, CR=microfiber': 31},
     {'actual_pm t=674, CR=microfiber': 31},
     {'actual_pm t=675, CR=microfiber': 32},
     {'actual_pm t=676, CR=microfiber': 32},
     {'actual_pm t=677, CR=microfiber': 32},
     {'actual_pm t=678, CR=microfiber': 32},
     {'actual_pm t=679, CR=microfiber': 32},
     {'actual_pm t=680, CR=microfiber': 31},
     {'actual_pm t=681, CR=microfiber': 31},
     {'actual_pm t=682, CR=microfiber': 31},
     {'actual_pm t=683, CR=microfiber': 31},
     {'actual_pm t=684, CR=microfiber': 31},
     {'actual_pm t=685, CR=microfiber': 29},
     {'actual_pm t=686, CR=microfiber': 31},
     {'actual_pm t=687, CR=microfiber': 31},
     {'actual_pm t=688, CR=microfiber': 31},
     {'actual_pm t=689, CR=microfiber': 31},
     {'actual_pm t=690, CR=microfiber': 31},
     {'actual_pm t=691, CR=microfiber': 29},
     {'actual_pm t=692, CR=microfiber': 29},
     {'actual_pm t=693, CR=microfiber': 30},
     {'actual_pm t=694, CR=microfiber': 30},
     {'actual_pm t=695, CR=microfiber': 30},
     {'actual_pm t=696, CR=microfiber': 30},
     {'actual_pm t=697, CR=microfiber': 30},
     {'actual_pm t=698, CR=microfiber': 29},
     {'actual_pm t=699, CR=microfiber': 31},
     {'actual_pm t=700, CR=microfiber': 31},
     {'actual_pm t=701, CR=microfiber': 31},
     {'actual_pm t=702, CR=microfiber': 31},
     {'actual_pm t=703, CR=microfiber': 31},
     {'actual_pm t=704, CR=microfiber': 31},
     {'actual_pm t=705, CR=microfiber': 32},
     {'actual_pm t=706, CR=microfiber': 32},
     {'actual_pm t=707, CR=microfiber': 32},
     {'actual_pm t=708, CR=microfiber': 32},
     {'actual_pm t=709, CR=microfiber': 32},
     {'actual_pm t=710, CR=microfiber': 32},
     {'actual_pm t=711, CR=microfiber': 32},
     {'actual_pm t=712, CR=microfiber': 32},
     {'actual_pm t=713, CR=microfiber': 32},
     {'actual_pm t=714, CR=microfiber': 32},
     {'actual_pm t=715, CR=microfiber': 32},
     {'actual_pm t=716, CR=microfiber': 32},
     {'actual_pm t=717, CR=microfiber': 32},
     {'actual_pm t=718, CR=microfiber': 32},
     {'actual_pm t=719, CR=microfiber': 32},
     {'actual_pm t=720, CR=microfiber': 32},
     {'actual_pm t=721, CR=microfiber': 32},
     {'actual_pm t=722, CR=microfiber': 32},
     {'actual_pm t=723, CR=microfiber': 32},
     {'actual_pm t=724, CR=microfiber': 30},
     {'actual_pm t=725, CR=microfiber': 31},
     {'actual_pm t=726, CR=microfiber': 31},
     {'actual_pm t=727, CR=microfiber': 31},
     {'actual_pm t=728, CR=microfiber': 31},
     {'actual_pm t=729, CR=microfiber': 30},
     {'actual_pm t=730, CR=microfiber': 30},
     {'actual_pm t=731, CR=microfiber': 32},
     {'actual_pm t=732, CR=microfiber': 32},
     {'actual_pm t=733, CR=microfiber': 30},
     {'actual_pm t=734, CR=microfiber': 31},
     {'actual_pm t=735, CR=microfiber': 31},
     {'actual_pm t=736, CR=microfiber': 31},
     {'actual_pm t=737, CR=microfiber': 30},
     {'actual_pm t=738, CR=microfiber': 30},
     {'actual_pm t=739, CR=microfiber': 30},
     {'actual_pm t=740, CR=microfiber': 30},
     {'actual_pm t=741, CR=microfiber': 30},
     {'actual_pm t=742, CR=microfiber': 29},
     {'actual_pm t=743, CR=microfiber': 29},
     {'actual_pm t=744, CR=microfiber': 30},
     {'actual_pm t=745, CR=microfiber': 30},
     {'actual_pm t=746, CR=microfiber': 30},
     {'actual_pm t=747, CR=microfiber': 30},
     {'actual_pm t=748, CR=microfiber': 30},
     {'actual_pm t=749, CR=microfiber': 29},
     {'actual_pm t=750, CR=microfiber': 29},
     {'actual_pm t=751, CR=microfiber': 29},
     {'actual_pm t=752, CR=microfiber': 29},
     {'actual_pm t=753, CR=microfiber': 29},
     {'actual_pm t=754, CR=microfiber': 29},
     {'actual_pm t=755, CR=microfiber': 29},
     {'actual_pm t=756, CR=microfiber': 30},
     {'actual_pm t=757, CR=microfiber': 30},
     {'actual_pm t=758, CR=microfiber': 30},
     {'actual_pm t=759, CR=microfiber': 30},
     {'actual_pm t=760, CR=microfiber': 30},
     {'actual_pm t=761, CR=microfiber': 32},
     {'actual_pm t=762, CR=microfiber': 32},
     {'actual_pm t=763, CR=microfiber': 30},
     {'actual_pm t=764, CR=microfiber': 31},
     {'actual_pm t=765, CR=microfiber': 31},
     {'actual_pm t=766, CR=microfiber': 31},
     {'actual_pm t=767, CR=microfiber': 30},
     {'actual_pm t=768, CR=microfiber': 30},
     {'actual_pm t=769, CR=microfiber': 30},
     {'actual_pm t=770, CR=microfiber': 30},
     {'actual_pm t=771, CR=microfiber': 30},
     {'actual_pm t=772, CR=microfiber': 30},
     {'actual_pm t=773, CR=microfiber': 31},
     {'actual_pm t=774, CR=microfiber': 29},
     {'actual_pm t=775, CR=microfiber': 29},
     {'actual_pm t=776, CR=microfiber': 29},
     {'actual_pm t=777, CR=microfiber': 29},
     {'actual_pm t=778, CR=microfiber': 29},
     {'actual_pm t=779, CR=microfiber': 29},
     {'actual_pm t=780, CR=microfiber': 29},
     {'actual_pm t=781, CR=microfiber': 29},
     {'actual_pm t=782, CR=microfiber': 29},
     {'actual_pm t=783, CR=microfiber': 29},
     {'actual_pm t=784, CR=microfiber': 29},
     {'actual_pm t=785, CR=microfiber': 29},
     {'actual_pm t=786, CR=microfiber': 30},
     {'actual_pm t=787, CR=microfiber': 30},
     {'actual_pm t=788, CR=microfiber': 30},
     {'actual_pm t=789, CR=microfiber': 30},
     {'actual_pm t=790, CR=microfiber': 30},
     {'actual_pm t=791, CR=microfiber': 30},
     {'actual_pm t=792, CR=microfiber': 30},
     {'actual_pm t=793, CR=microfiber': 30},
     {'actual_pm t=794, CR=microfiber': 31},
     {'actual_pm t=795, CR=microfiber': 31},
     {'actual_pm t=796, CR=microfiber': 31},
     {'actual_pm t=797, CR=microfiber': 31},
     {'actual_pm t=798, CR=microfiber': 31},
     {'actual_pm t=799, CR=microfiber': 31},
     {'actual_pm t=800, CR=microfiber': 31},
     {'actual_pm t=801, CR=microfiber': 31},
     {'actual_pm t=802, CR=microfiber': 31},
     {'actual_pm t=803, CR=microfiber': 33},
     {'actual_pm t=804, CR=microfiber': 33},
     {'actual_pm t=805, CR=microfiber': 33},
     {'actual_pm t=806, CR=microfiber': 32},
     {'actual_pm t=807, CR=microfiber': 32},
     {'actual_pm t=808, CR=microfiber': 32},
     {'actual_pm t=809, CR=microfiber': 32},
     {'actual_pm t=810, CR=microfiber': 32},
     {'actual_pm t=811, CR=microfiber': 32},
     {'actual_pm t=812, CR=microfiber': 32},
     {'actual_pm t=813, CR=microfiber': 32},
     {'actual_pm t=814, CR=microfiber': 32},
     {'actual_pm t=815, CR=microfiber': 32},
     {'actual_pm t=816, CR=microfiber': 32},
     {'actual_pm t=817, CR=microfiber': 32},
     {'actual_pm t=818, CR=microfiber': 32},
     {'actual_pm t=819, CR=microfiber': 32},
     {'actual_pm t=820, CR=microfiber': 32},
     {'actual_pm t=821, CR=microfiber': 32},
     {'actual_pm t=822, CR=microfiber': 32},
     {'actual_pm t=823, CR=microfiber': 32},
     {'actual_pm t=824, CR=microfiber': 32},
     {'actual_pm t=825, CR=microfiber': 30},
     {'actual_pm t=826, CR=microfiber': 30},
     {'actual_pm t=827, CR=microfiber': 29},
     {'actual_pm t=828, CR=microfiber': 29},
     {'actual_pm t=829, CR=microfiber': 29},
     {'actual_pm t=830, CR=microfiber': 31},
     {'actual_pm t=831, CR=microfiber': 30},
     {'actual_pm t=832, CR=microfiber': 30},
     {'actual_pm t=833, CR=microfiber': 29},
     {'actual_pm t=834, CR=microfiber': 29},
     {'actual_pm t=835, CR=microfiber': 29},
     {'actual_pm t=836, CR=microfiber': 29},
     {'actual_pm t=837, CR=microfiber': 29},
     {'actual_pm t=838, CR=microfiber': 29},
     {'actual_pm t=839, CR=microfiber': 29},
     {'actual_pm t=840, CR=microfiber': 29},
     {'actual_pm t=841, CR=microfiber': 29},
     {'actual_pm t=842, CR=microfiber': 29},
     {'actual_pm t=843, CR=microfiber': 29},
     {'actual_pm t=844, CR=microfiber': 29},
     {'actual_pm t=845, CR=microfiber': 29},
     {'actual_pm t=846, CR=microfiber': 29},
     {'actual_pm t=847, CR=microfiber': 29},
     {'actual_pm t=848, CR=microfiber': 29},
     {'actual_pm t=849, CR=microfiber': 29},
     {'actual_pm t=850, CR=microfiber': 29},
     {'actual_pm t=851, CR=microfiber': 29},
     {'actual_pm t=852, CR=microfiber': 30},
     {'actual_pm t=853, CR=microfiber': 31},
     {'actual_pm t=854, CR=microfiber': 31},
     {'actual_pm t=855, CR=microfiber': 31},
     {'actual_pm t=856, CR=microfiber': 31},
     {'actual_pm t=857, CR=microfiber': 30},
     {'actual_pm t=858, CR=microfiber': 30},
     {'actual_pm t=859, CR=microfiber': 30},
     {'actual_pm t=860, CR=microfiber': 30},
     {'actual_pm t=861, CR=microfiber': 30},
     {'actual_pm t=862, CR=microfiber': 30},
     {'actual_pm t=863, CR=microfiber': 30},
     {'actual_pm t=864, CR=microfiber': 30},
     {'actual_pm t=865, CR=microfiber': 30},
     {'actual_pm t=866, CR=microfiber': 30},
     {'actual_pm t=867, CR=microfiber': 30},
     {'actual_pm t=868, CR=microfiber': 30},
     {'actual_pm t=869, CR=microfiber': 30},
     {'actual_pm t=870, CR=microfiber': 30},
     {'actual_pm t=871, CR=microfiber': 30},
     {'actual_pm t=872, CR=microfiber': 29},
     {'actual_pm t=873, CR=microfiber': 29},
     {'actual_pm t=874, CR=microfiber': 28},
     {'actual_pm t=875, CR=microfiber': 28},
     {'actual_pm t=876, CR=microfiber': 28},
     {'actual_pm t=877, CR=microfiber': 28},
     {'actual_pm t=878, CR=microfiber': 27},
     {'actual_pm t=879, CR=microfiber': 27},
     {'actual_pm t=880, CR=microfiber': 28},
     {'actual_pm t=881, CR=microfiber': 29},
     {'actual_pm t=882, CR=microfiber': 29},
     {'actual_pm t=883, CR=microfiber': 31},
     {'actual_pm t=884, CR=microfiber': 31},
     {'actual_pm t=885, CR=microfiber': 29},
     {'actual_pm t=886, CR=microfiber': 29},
     {'actual_pm t=887, CR=microfiber': 29},
     {'actual_pm t=888, CR=microfiber': 30},
     {'actual_pm t=889, CR=microfiber': 30},
     {'actual_pm t=890, CR=microfiber': 30},
     {'actual_pm t=891, CR=microfiber': 30},
     {'actual_pm t=892, CR=microfiber': 30},
     {'actual_pm t=893, CR=microfiber': 30},
     {'actual_pm t=894, CR=microfiber': 29},
     {'actual_pm t=895, CR=microfiber': 30},
     {'actual_pm t=896, CR=microfiber': 30},
     {'actual_pm t=897, CR=microfiber': 30},
     {'actual_pm t=898, CR=microfiber': 31},
     {'actual_pm t=899, CR=microfiber': 31},
     {'actual_pm t=900, CR=microfiber': 31},
     {'actual_pm t=901, CR=microfiber': 31},
     {'actual_pm t=902, CR=microfiber': 30},
     {'actual_pm t=903, CR=microfiber': 30},
     {'actual_pm t=904, CR=microfiber': 30},
     {'actual_pm t=905, CR=microfiber': 30},
     {'actual_pm t=906, CR=microfiber': 30},
     {'actual_pm t=907, CR=microfiber': 30},
     {'actual_pm t=908, CR=microfiber': 30},
     {'actual_pm t=909, CR=microfiber': 29},
     {'actual_pm t=910, CR=microfiber': 29},
     {'actual_pm t=911, CR=microfiber': 28},
     {'actual_pm t=912, CR=microfiber': 28},
     {'actual_pm t=913, CR=microfiber': 28},
     {'actual_pm t=914, CR=microfiber': 28},
     {'actual_pm t=915, CR=microfiber': 27},
     {'actual_pm t=916, CR=microfiber': 28},
     {'actual_pm t=917, CR=microfiber': 27},
     {'actual_pm t=918, CR=microfiber': 28},
     {'actual_pm t=919, CR=microfiber': 28},
     {'actual_pm t=920, CR=microfiber': 28},
     {'actual_pm t=921, CR=microfiber': 28},
     {'actual_pm t=922, CR=microfiber': 28},
     {'actual_pm t=923, CR=microfiber': 28},
     {'actual_pm t=924, CR=microfiber': 29},
     {'actual_pm t=925, CR=microfiber': 29},
     {'actual_pm t=926, CR=microfiber': 29},
     {'actual_pm t=927, CR=microfiber': 29},
     {'actual_pm t=928, CR=microfiber': 31},
     {'actual_pm t=929, CR=microfiber': 30},
     {'actual_pm t=930, CR=microfiber': 30},
     {'actual_pm t=931, CR=microfiber': 30},
     {'actual_pm t=932, CR=microfiber': 30},
     {'actual_pm t=933, CR=microfiber': 30},
     {'actual_pm t=934, CR=microfiber': 31},
     {'actual_pm t=935, CR=microfiber': 31},
     {'actual_pm t=936, CR=microfiber': 31},
     {'actual_pm t=937, CR=microfiber': 31},
     {'actual_pm t=938, CR=microfiber': 31},
     {'actual_pm t=939, CR=microfiber': 29},
     {'actual_pm t=940, CR=microfiber': 29},
     {'actual_pm t=941, CR=microfiber': 29},
     {'actual_pm t=942, CR=microfiber': 29},
     {'actual_pm t=943, CR=microfiber': 29},
     {'actual_pm t=944, CR=microfiber': 28},
     {'actual_pm t=945, CR=microfiber': 28},
     {'actual_pm t=946, CR=microfiber': 27},
     {'actual_pm t=947, CR=microfiber': 27},
     {'actual_pm t=948, CR=microfiber': 27},
     {'actual_pm t=949, CR=microfiber': 26},
     {'actual_pm t=950, CR=microfiber': 26},
     {'actual_pm t=951, CR=microfiber': 26},
     {'actual_pm t=952, CR=microfiber': 27},
     {'actual_pm t=953, CR=microfiber': 27},
     {'actual_pm t=954, CR=microfiber': 27},
     {'actual_pm t=955, CR=microfiber': 28},
     {'actual_pm t=956, CR=microfiber': 26},
     {'actual_pm t=957, CR=microfiber': 26},
     {'actual_pm t=958, CR=microfiber': 28},
     {'actual_pm t=959, CR=microfiber': 28},
     {'actual_pm t=960, CR=microfiber': 28},
     {'actual_pm t=961, CR=microfiber': 28},
     {'actual_pm t=962, CR=microfiber': 29},
     {'actual_pm t=963, CR=microfiber': 29},
     {'actual_pm t=964, CR=microfiber': 29},
     {'actual_pm t=965, CR=microfiber': 29},
     {'actual_pm t=966, CR=microfiber': 29},
     {'actual_pm t=967, CR=microfiber': 29},
     {'actual_pm t=968, CR=microfiber': 29},
     {'actual_pm t=969, CR=microfiber': 29},
     {'actual_pm t=970, CR=microfiber': 29},
     {'actual_pm t=971, CR=microfiber': 29},
     {'actual_pm t=972, CR=microfiber': 29},
     {'actual_pm t=973, CR=microfiber': 29},
     {'actual_pm t=974, CR=microfiber': 29},
     {'actual_pm t=975, CR=microfiber': 28},
     {'actual_pm t=976, CR=microfiber': 28},
     {'actual_pm t=977, CR=microfiber': 28},
     {'actual_pm t=978, CR=microfiber': 27},
     {'actual_pm t=979, CR=microfiber': 27},
     {'actual_pm t=980, CR=microfiber': 27},
     {'actual_pm t=981, CR=microfiber': 27},
     {'actual_pm t=982, CR=microfiber': 27},
     {'actual_pm t=983, CR=microfiber': 27},
     {'actual_pm t=984, CR=microfiber': 27},
     {'actual_pm t=985, CR=microfiber': 27},
     {'actual_pm t=986, CR=microfiber': 27},
     {'actual_pm t=987, CR=microfiber': 27},
     {'actual_pm t=988, CR=microfiber': 27},
     {'actual_pm t=989, CR=microfiber': 27},
     {'actual_pm t=990, CR=microfiber': 28},
     {'actual_pm t=991, CR=microfiber': 28},
     {'actual_pm t=992, CR=microfiber': 28},
     {'actual_pm t=993, CR=microfiber': 28},
     {'actual_pm t=994, CR=microfiber': 28},
     {'actual_pm t=995, CR=microfiber': 28},
     {'actual_pm t=996, CR=microfiber': 28},
     {'actual_pm t=997, CR=microfiber': 28},
     {'actual_pm t=998, CR=microfiber': 27},
     {'actual_pm t=999, CR=microfiber': 28},
     ...]

```python
def get_anemometer_readings():
    return pd.DataFrame([
        {
            'layer': 0,
            'group': 0,
            'reading': 0.4
        },
        {
            'layer': 0,
            'group': 0,
            'reading': 0.5
        },
        {
            'layer': 0,
            'group': 1,
            'reading': 1.2
        },
        {
            'layer': 0,
            'group': 1,
            'reading': 1.6
        },
        {
            'layer': 0,
            'group': 2,
            'reading': 1.1
        },
        {
            'layer': 0,
            'group': 2,
            'reading': 1.1
        },
        {
            'layer': 0,
            'group': 3,
            'reading': 1.3
        },
        {
            'layer': 0,
            'group': 3,
            'reading': 1.2
        },
        {
            'layer': 0,
            'group': 4,
            'reading': 0.3
        },
        {
            'layer': 0,
            'group': 4,
            'reading': 0.9
        },
        {
            'layer': 0,
            'group': 5,
            'reading': 0.3
        },
        {
            'layer': 0,
            'group': 5,
            'reading': 0.5
        },
        {
            'layer': 0,
            'group': 6,
            'reading': 0.3
        },
        {
            'layer': 0,
            'group': 6,
            'reading': 0.5
        },
        {
            'layer': 0,
            'group': 7,
            'reading': 0.5
        },
        {
            'layer': 0,
            'group': 7,
            'reading': 0.4
        },
        {
            'layer': 0,
            'group': 8,
            'reading': 1.3
        },
        {
            'layer': 0,
            'group': 8,
            'reading': 1.3
        },
        {
            'layer': 0,
            'group': 9,
            'reading': 1.5
        },
        {
            'layer': 0,
            'group': 9,
            'reading': 1.3
        },
        {
            'layer': 0,
            'group': 10,
            'reading': 1.2
        },
        {
            'layer': 0,
            'group': 10,
            'reading': 1.7
        },
        {
            'layer': 0,
            'group': 11,
            'reading': 1.6
        },
        {
            'layer': 0,
            'group': 11,
            'reading': 1.0
        },

        {
            'layer': 1,
            'group': 0,
            'reading': 2.8
        },
        {
            'layer': 1,
            'group': 0,
            'reading': 2.1
        },
        {
            'layer': 1,
            'group': 1,
            'reading': 2.7
        },
        {
            'layer': 1,
            'group': 1,
            'reading': 2.1
        },
        {
            'layer': 1,
            'group': 2,
            'reading': 2.0
        },
        {
            'layer': 1,
            'group': 2,
            'reading': 2.2
        },
        {
            'layer': 1,
            'group': 3,
            'reading': 3.0
        },
        {
            'layer': 1,
            'group': 3,
            'reading': 3.1
        },
        {
            'layer': 1,
            'group': 4,
            'reading': 2.4
        },
        {
            'layer': 1,
            'group': 4,
            'reading': 2.4
        },
        {
            'layer': 1,
            'group': 5,
            'reading': 2.5
        },
        {
            'layer': 1,
            'group': 5,
            'reading': 2.5
        },
        {
            'layer': 1,
            'group': 6,
            'reading': 1.6
        },
        {
            'layer': 1,
            'group': 6,
            'reading': 2.2
        },
        {
            'layer': 1,
            'group': 7,
            'reading': 2.5
        },
        {
            'layer': 1,
            'group': 7,
            'reading': 2.3
        },
        {
            'layer': 1,
            'group': 8,
            'reading': 2.4
        },
        {
            'layer': 1,
            'group': 8,
            'reading': 2.5
        },
        {
            'layer': 1,
            'group': 9,
            'reading': 2.4
        },
        {
            'layer': 1,
            'group': 9,
            'reading': 2.0
        },
        {
            'layer': 1,
            'group': 10,
            'reading': 2.0
        },
        {
            'layer': 1,
            'group': 10,
            'reading': 2.6
        },
        {
            'layer': 1,
            'group': 11,
            'reading': 2.4
        },
        {
            'layer': 1,
            'group': 11,
            'reading': 2.4
        },

        {
            'layer': 2,
            'group': 0,
            'reading': 1.2
        },
        {
            'layer': 2,
            'group': 0,
            'reading': 1.0
        },
        {
            'layer': 2,
            'group': 1,
            'reading': 1.1
        },
        {
            'layer': 2,
            'group': 1,
            'reading': 1.0
        },
        {
            'layer': 2,
            'group': 2,
            'reading': 1.2
        },
        {
            'layer': 2,
            'group': 2,
            'reading': 0.6
        },
        {
            'layer': 2,
            'group': 3,
            'reading': 1.2
        },
        {
            'layer': 2,
            'group': 3,
            'reading': 1.2
        },
        {
            'layer': 2,
            'group': 4,
            'reading': 1.7
        },
        {
            'layer': 2,
            'group': 4,
            'reading': 1.2
        },
        {
            'layer': 2,
            'group': 5,
            'reading': 0.6
        },
        {
            'layer': 2,
            'group': 5,
            'reading': 0.7
        },
        {
            'layer': 2,
            'group': 6,
            'reading': 0.4
        },
        {
            'layer': 2,
            'group': 6,
            'reading': 0.4
        },
        {
            'layer': 2,
            'group': 7,
            'reading': 1.6
        },
        {
            'layer': 2,
            'group': 7,
            'reading': 1.0
        },
        {
            'layer': 2,
            'group': 8,
            'reading': 1.1
        },
        {
            'layer': 2,
            'group': 8,
            'reading': 1.1
        },
        {
            'layer': 2,
            'group': 9,
            'reading': 1.0
        },
        {
            'layer': 2,
            'group': 9,
            'reading': 0.9
        },
        {
            'layer': 2,
            'group': 10,
            'reading': 0.5
        },
        {
            'layer': 2,
            'group': 10,
            'reading': 1.1
        },
        {
            'layer': 2,
            'group': 11,
            'reading': 0.9
        },
        {
            'layer': 2,
            'group': 11,
            'reading': 1.0
        }
    ])

```

```python
power_df = generate_power_df()
measured_pm_df = generate_measured_pm()
coway_mps = pd.DataFrame(
    [
        {'x': 0, 'y': 0, "measured_mps": 10.5},
        {'x': 0, 'y': 1, "measured_mps": 10.4},
        {'x': 0, 'y': 2, "measured_mps": 8.4},
        {'x': 1, 'y': 0, "measured_mps": 9.8},
        {'x': 1, 'y': 1, "measured_mps": 11.8},
        {'x': 1, 'y': 2, "measured_mps": 10.1},
        {'x': 2, 'y': 0, "measured_mps": 8.6},
        {'x': 2, 'y': 1, "measured_mps": 9.8},
        {'x': 2, 'y': 2, "measured_mps": 8.6},
        {'x': 3, 'y': 0, "measured_mps": 8.6},
        {'x': 3, 'y': 1, "measured_mps": 10.1},
        {'x': 3, 'y': 2, "measured_mps": 8.6},
    ]
)
coway_cfps = compute_cfps_rectangular_air_cleaner(
    num_sub_sections=coway_mps.shape[0],
    mps=coway_mps,
    width_in=4.75,
    height_in=3.75,
)
cfps = pd.DataFrame({'cfps': coway_cfps.round(1)})
cfps['value'] = 0
coway_cfps_normalized = normalize(
    cfps.rename(columns={'cfps': 'AP-1512HH cfps'}), x='AP-1512HH cfps'
)
coway_cfps_norm = coway_cfps_normalized.reset_index()
cadr_df = generate_cadr_df()
anemometer_readings = get_anemometer_readings()
anemometer_reading_df = create_anemometer_cpt_df(10000)
airflow_cpts = generate_cpts_for_anemometer_readings(
    number_of_readings=2,
    anemometer_reading_df=anemometer_reading_df,
    given='actual_mps',
    outcome='measured_mps',
    storage_path=storage_path
)
actual_mps_priors = generate_flat_priors(0, 5, 0.1, 'actual_mps')
bayesian_network = BN(
    cpts=airflow_cpts,
    priors=[
        CPT(
            ParquetData(
                actual_mps_priors,
                storage_folder=storage_path
            ),
            outcomes=['actual_mps']
        )
    ],
)
adr_microfiber = sample_total_air_delivery_rate(
    bayesian_network=bayesian_network,
    anemometer_readings=anemometer_readings
)

decay_df = generate_flat_prior(
    np.arange(0, 1, 0.01),
    prior_name='decay'
)

decay_df['decay'] = decay_df['decay'].round(2)
decay_power_multiplier_df = join_two_df(
    column_1='decay',
    column_1_values=np.arange(0, 1, 0.01),
    column_2='power',
    column_2_values=np.arange(0, 0.02, 0.001),
    column_outcome='decay_power_multiplier',
    func=lambda df: (df['decay'] ** df['power']).round(3)
)

decay_power_multiplier_df['decay'] = \
    decay_power_multiplier_df['decay'].round(2)
decay_power_multiplier_df['power'] = \
    decay_power_multiplier_df['power'].round(3)
filtration_eff_df = generate_flat_prior(
    np.arange(0, 1, 0.01),
    prior_name='filtration_eff'
)

filtration_eff_df['filtration_eff'] = filtration_eff_df['filtration_eff']\
    .round(2)

actual_pm_df = join_two_df(
    column_1='decay_power_multiplier',
    column_1_values=np.arange(0, 1, 0.001),
    column_2='actual_pm_t-1',
    column_2_values=list(range(0, 101)),
    column_outcome='actual_pm_t',
    func=lambda df: (
        df['actual_pm_t-1'] * df['decay_power_multiplier']
    ).round().astype('int16')
)
adr_df = generate_flat_prior(
    np.arange(0, 12, 0.1),
    prior_name='adr_cfps'
)

adr_df['adr_cfps'] = adr_df['adr_cfps'].round(1)
adr_microfiber_df = pd.DataFrame({
    'adr_cfps_microfiber': adr_microfiber.round(1),
    'value': 0
})

priors = []
cpts = []

priors.append(
    CPT(
        ParquetData(
            coway_cfps_norm.groupby('AP-1512HH cfps').sum().
            reset_index().rename(
                columns={'AP-1512HH cfps': 'cadr_cfps AP-1512HH'}
            ),
            storage_folder=storage_path
        ),
        outcomes=['cadr_cfps AP-1512HH']
    )
)

cpts.append(
    CPT(
        ParquetData(
            power_df.rename(columns={
                'cadr_cfps': 'cadr_cfps AP-1512HH',
                'power': 'power AP-1512HH'
            }),
            storage_folder=storage_path
        ),
        outcomes=['power AP-1512HH'],
        givens=['cadr_cfps AP-1512HH', 'room_vol']
    )
)

priors.append(
    CPT(
        ParquetData(
            decay_df,
            storage_folder=storage_path
        ),
        outcomes=['decay']
    )
)

priors.append(
    CPT(
        ParquetData(
            filtration_eff_df.rename(
                columns={'filtration_eff': 'filtration_eff CR=microfiber'}
            ),
            storage_folder=storage_path
        ),
        outcomes=['filtration_eff CR=microfiber']
    )
)

priors.append(
    CPT(
        ParquetData(
            normalize(
                adr_microfiber_df,
                x='adr_cfps_microfiber'
            ).reset_index().rename(
                columns={'adr_cfps_microfiber': 'adr_cfps CR=microfiber'}
            ),
            storage_folder=storage_path
        ),
        outcomes=['adr_cfps CR=microfiber']
    )
)

cpts.append(
    CPT(
        ParquetData(
            cadr_df.rename(columns={
                'cadr_cfps': 'cadr_cfps CR=microfiber',
                'adr_cfps': 'adr_cfps CR=microfiber',
                'filtration_eff': 'filtration_eff CR=microfiber'
            }),
            storage_folder=storage_path
        ),
        outcomes=['cadr_cfps CR=microfiber'],
        givens=['adr_cfps CR=microfiber', 'filtration_eff CR=microfiber']
    )
)

cpts.append(
    CPT(
        ParquetData(
            power_df.rename(columns={
                'cadr_cfps': 'cadr_cfps CR=microfiber',
                'power': 'power CR=microfiber'
            }),
            storage_folder=storage_path
        ),
        outcomes=['power CR=microfiber'],
        givens=['cadr_cfps CR=microfiber', 'room_vol']
    )
)

cpts.append(
    CPT(
        ParquetData(
            decay_power_multiplier_df.rename(
                columns={
                    'decay_power_multiplier': 'decay_power_multiplier CR=microfiber',
                    'power': 'power CR=microfiber'
                }
            ),
            storage_folder=storage_path
        ),
        outcomes=['decay_power_multiplier CR=microfiber'],
        givens=['decay', 'power CR=microfiber']
    )
)

cpts.append(
    CPT(
        ParquetData(
            decay_power_multiplier_df.rename(
                columns={
                    'decay_power_multiplier': 'decay_power_multiplier AP-1512HH',
                    'power': 'power AP-1512HH'
                }
            ),
            storage_folder=storage_path
        ),
        outcomes=['decay_power_multiplier AP-1512HH'],
        givens=['decay', 'power AP-1512HH']
    )
)

cleaner_sec_mapping = {
    "CR=microfiber": 3600,
    "AP-1512HH": particle_counter_data.loc['2021-12-18 22:03:00':'2021-12-18 22:06:38'].shape[0]
}

for name, sec in cleaner_sec_mapping.items():
    for i in range(sec):
        cpts.append(
            CPT(
                ParquetData(
                    actual_pm_df.rename(
                        columns={
                            'actual_pm_t-1': f'actual_pm t={i}, {name}',
                            'actual_pm_t': f'actual_pm t={i+1}, {name}',
                            'decay_power_multiplier':
                                f'decay_power_multiplier {name}'
                        }
                    ),
                    storage_folder=storage_path
                ),
                outcomes=[f'actual_pm t={i+1}, {name}'],
                givens=[
                    f'decay_power_multiplier {name}',
                    f'actual_pm t={i}, {name}'
                ]
            )
        )

        cpts.append(
            CPT(
                ParquetData(
                    measured_pm_df.rename(
                        columns={
                            'measured_pm': f'measured_pm t={i}, {name}',
                            'actual_pm': f'actual_pm t={i}, {name}'
                        }
                    ),
                    storage_folder=storage_path
                ),
                outcomes=[f'measured_pm t={i}, {name}'],
                givens=[f'actual_pm t={i}, {name}']
            )
        )

bn = BN(
    cpts=cpts,
    priors=priors,
)

evidence = generate_evidence(only_one_reading, cleaner_name='CR=microfiber', row_col='pm1_0') + \
    generate_evidence(
        particle_counter_data.loc[
            '2021-12-18 22:03:00':'2021-12-18 22:06:38'
        ].reset_index(),
        cleaner_name='AP-1512HH',
        row_col='pm1_0'
    )

inference = VE(
    network=bn,
    query=Query(
        outcomes=["filtration_eff CR=microfiber"],
        givens=evidence
    )
)

result = inference.compute()

```

```python
bn = BN(
    cpts=cpts,
    priors=priors,
    graphviz_dag=graphviz.Digraph()
)
```

```python
bn.graphviz_dag
```

```python

result = VE(
    network=bn,
    query=Query(
        outcomes=["filtration_eff_microfiber"],
        givens=[
            {
                'measured_pm t=1, CR=microfiber': 49
            },
            {
                'measured_pm t=0, CR=microfiber': 50
            },
            {
                'room_vol': 720
            },
            {
                'adr_cfps CR=microfiber': 3.8
            }
        ])
).compute()
```

```python
get_df = result.get_df()
```

```python
get_df
```

      filtration_eff_microfiber
      room_vol
      adr_cfps_microfiber
      measured_pm t=1, CR=microfiber
      measured_pm t=0, CR=microfiber
      value

      0
      0.10
      720
      3.8
      49
      50
      0.014142

      1
      0.11
      720
      3.8
      49
      50
      0.014142

      2
      0.12
      720
      3.8
      49
      50
      0.014142

      3
      0.13
      720
      3.8
      49
      50
      0.014142

      4
      0.14
      720
      3.8
      49
      50
      0.014142

      5
      0.20
      720
      3.8
      49
      50
      0.014142

      6
      0.21
      720
      3.8
      49
      50
      0.014142

      7
      0.22
      720
      3.8
      49
      50
      0.014142

      8
      0.23
      720
      3.8
      49
      50
      0.014142

      9
      0.24
      720
      3.8
      49
      50
      0.014142

      10
      0.25
      720
      3.8
      49
      50
      0.014142

      11
      0.26
      720
      3.8
      49
      50
      0.014142

      12
      0.27
      720
      3.8
      49
      50
      0.014142

      13
      0.28
      720
      3.8
      49
      50
      0.016794

      14
      0.29
      720
      3.8
      49
      50
      0.016794

      15
      0.30
      720
      3.8
      49
      50
      0.016794

      16
      0.33
      720
      3.8
      49
      50
      0.016794

      17
      0.34
      720
      3.8
      49
      50
      0.016794

      18
      0.35
      720
      3.8
      49
      50
      0.016794

      19
      0.39
      720
      3.8
      49
      50
      0.016794

      20
      0.40
      720
      3.8
      49
      50
      0.016794

      21
      0.41
      720
      3.8
      49
      50
      0.016794

      22
      0.42
      720
      3.8
      49
      50
      0.016794

      23
      0.43
      720
      3.8
      49
      50
      0.016794

      24
      0.47
      720
      3.8
      49
      50
      0.016794

      25
      0.48
      720
      3.8
      49
      50
      0.016794

      26
      0.52
      720
      3.8
      49
      50
      0.018267

      27
      0.53
      720
      3.8
      49
      50
      0.018267

      28
      0.54
      720
      3.8
      49
      50
      0.018267

      29
      0.55
      720
      3.8
      49
      50
      0.018267

      30
      0.56
      720
      3.8
      49
      50
      0.018267

      31
      0.57
      720
      3.8
      49
      50
      0.018267

      32
      0.58
      720
      3.8
      49
      50
      0.018267

      33
      0.59
      720
      3.8
      49
      50
      0.018267

      34
      0.65
      720
      3.8
      49
      50
      0.018267

      35
      0.66
      720
      3.8
      49
      50
      0.018267

      36
      0.67
      720
      3.8
      49
      50
      0.018267

      37
      0.68
      720
      3.8
      49
      50
      0.018563

      38
      0.69
      720
      3.8
      49
      50
      0.018563

      39
      0.70
      720
      3.8
      49
      50
      0.018563

      40
      0.71
      720
      3.8
      49
      50
      0.018563

      41
      0.72
      720
      3.8
      49
      50
      0.018563

      42
      0.78
      720
      3.8
      49
      50
      0.018563

      43
      0.79
      720
      3.8
      49
      50
      0.018563

      44
      0.80
      720
      3.8
      49
      50
      0.018563

      45
      0.81
      720
      3.8
      49
      50
      0.018563

      46
      0.82
      720
      3.8
      49
      50
      0.018563

      47
      0.83
      720
      3.8
      49
      50
      0.018563

      48
      0.84
      720
      3.8
      49
      50
      0.018563

      49
      0.85
      720
      3.8
      49
      50
      0.018563

      50
      0.91
      720
      3.8
      49
      50
      0.019447

      51
      0.92
      720
      3.8
      49
      50
      0.019447

      52
      0.93
      720
      3.8
      49
      50
      0.019447

      53
      0.94
      720
      3.8
      49
      50
      0.019447

      54
      0.95
      720
      3.8
      49
      50
      0.019447

      55
      0.96
      720
      3.8
      49
      50
      0.019447

      56
      0.97
      720
      3.8
      49
      50
      0.019447

      57
      0.98
      720
      3.8
      49
      50
      0.019447

```python
posterior_dist = pd.read_csv("./filt_eff_decay_microfiber.csv")
```

```python
np.random.choice(a=posterior_dist.index, size=10000, p=posterior_dist['value'])
```

    array([1052, 1125, 1334, ..., 1400, 1062, 1169])

```python
posterior_dist.loc[1052]
```

    Unnamed: 0                      1052.000000
    filtration_eff CR=microfiber       0.990000
    decay                              0.270000
    actual_pm t=0, CR=microfiber      29.000000
    value                              0.003208
    Name: 1052, dtype: float64

```python
indices = np.random.choice(a=posterior_dist.index, size=10000, p=posterior_dist['value'])

sims = []
for i in indices:
    eff = posterior_dist.loc[i]['filtration_eff CR=microfiber']
    decay = posterior_dist.loc[i]['decay']
    volume = 720
    cadr_cfps = 3.9
    t_0_actual = posterior_dist.loc[i]['actual_pm t=0, CR=microfiber']

    num_secs_in_half_an_hour = 1800

    t_next_actual = t_0_actual

    one_run_measured = []
    for j in range(num_secs_in_half_an_hour):
        t_next_actual = t_next_actual * (1.0-decay) ** (eff * cadr_cfps / volume)
        t_next_measured = np.random.normal(t_next_actual, 4)
        no_neg = (t_next_measured > 0) * t_next_measured
        one_run_measured.append(no_neg)

    sims.append(one_run_measured)
```

```python
df_posterior_predictive = pd.DataFrame(sims).T
```

```python
quantiles = df_posterior_predictive.quantile(q=[0.005, 0.995], axis=1)
```

```python
df_posterior_predictive
```

      0
      1
      2
      3
      4
      5
      6
      7
      8
      9
      ...
      9990
      9991
      9992
      9993
      9994
      9995
      9996
      9997
      9998
      9999

      0
      26.737828
      35.071482
      27.398449
      31.149138
      27.473565
      27.988212
      27.044035
      26.558105
      31.714264
      27.214015
      ...
      34.284007
      33.692196
      24.292933
      26.855935
      26.718296
      30.356450
      31.159757
      25.559801
      23.738163
      28.235905

      1
      27.364918
      32.671660
      26.672526
      18.274141
      33.396911
      31.955188
      26.911747
      30.976141
      28.240176
      24.138439
      ...
      28.993832
      32.533808
      18.819394
      30.453718
      34.166280
      38.185069
      26.772997
      28.553276
      26.544670
      28.538622

      2
      24.830156
      36.069853
      27.058753
      34.856856
      27.356131
      30.248566
      29.610459
      30.837934
      32.654343
      22.002600
      ...
      31.127872
      26.372373
      31.943486
      29.416828
      22.200456
      27.526893
      27.963436
      26.147879
      30.871467
      27.522976

      3
      23.441714
      31.499272
      26.381453
      31.745262
      33.908885
      32.297969
      29.894169
      28.324872
      31.625304
      27.300021
      ...
      28.614671
      32.436942
      31.425535
      29.944602
      25.975968
      20.949838
      32.563039
      33.586713
      38.390645
      26.442401

      4
      29.169841
      33.460167
      29.113209
      28.288619
      36.769249
      26.524607
      20.765274
      30.416899
      33.939968
      32.167713
      ...
      34.262927
      33.242311
      28.022853
      27.783147
      21.135468
      27.384757
      34.832119
      30.317467
      33.209214
      25.970417

      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...
      ...

      1795
      3.403008
      8.448555
      3.879770
      5.773277
      -0.000000
      -0.000000
      -0.000000
      3.472493
      -0.000000
      -0.000000
      ...
      0.577456
      -0.000000
      1.068409
      1.523675
      -0.000000
      8.537772
      3.500249
      1.984387
      6.170382
      -0.000000

      1796
      -0.000000
      -0.000000
      7.321969
      1.584707
      0.121301
      0.549714
      -0.000000
      -0.000000
      6.585590
      0.516105
      ...
      -0.000000
      1.537908
      1.110009
      -0.000000
      -0.000000
      -0.000000
      -0.000000
      -0.000000
      4.934338
      3.479660

      1797
      0.116935
      0.632883
      -0.000000
      -0.000000
      -0.000000
      -0.000000
      1.165043
      -0.000000
      3.317744
      -0.000000
      ...
      2.387918
      4.592017
      -0.000000
      2.386147
      -0.000000
      -0.000000
      6.022608
      -0.000000
      15.276962
      2.222581

      1798
      -0.000000
      3.490279
      -0.000000
      -0.000000
      1.102819
      3.108755
      -0.000000
      -0.000000
      4.634258
      0.102508
      ...
      2.715883
      0.236051
      -0.000000
      1.395831
      2.874540
      -0.000000
      8.908660
      3.228829
      1.805053
      7.472882

      1799
      5.647983
      7.091130
      0.204944
      -0.000000
      3.867454
      -0.000000
      -0.000000
      -0.000000
      -0.000000
      4.375704
      ...
      -0.000000
      -0.000000
      3.880948
      -0.000000
      -0.000000
      -0.000000
      -0.000000
      -0.000000
      6.551210
      4.312581

1800 rows × 10000 columns

```python
quantiles = quantiles.T
```

```python
quantiles
```

      0.005
      0.995

      0
      18.568192
      39.498053

      1
      18.468253
      39.127677

      2
      18.520708
      39.287536

      3
      18.552332
      38.989048

      4
      18.147018
      39.130046

      ...
      ...
      ...

      1795
      -0.000000
      11.161447

      1796
      -0.000000
      11.022309

      1797
      -0.000000
      10.928323

      1798
      -0.000000
      11.214486

      1799
      -0.000000
      10.834635

1800 rows × 2 columns

```python
fig, ax = plt.subplots()
ax.fill_between(x=list(range(1800)), y1=quantiles[0.005], y2=quantiles[0.995], alpha=0.5)
only_one_reading['pm1_0'].plot(ax=ax, color='r')
```

```python
get_df[['filtration_eff_microfiber', 'value']].plot.bar(x='filtration_eff_microfiber', y='value')
```

```python
sns.heatmap(get_df.pivot_table(values=['value'], index=["power"], columns=['decay']))
```

```python
actual_pm_df = generate_actual_pm_df()
```

```python
pd.Series(np.random.normal(10, 4, size=10000)).plot.kde()
```

```python
def measurement_df()
```