diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 3895e6df1..e208d94af 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.3.1 +current_version = 0.3.2 commit = True message = chore: bump covidcast-indicators to {new_version} tag = False diff --git a/_delphi_utils_python/.bumpversion.cfg b/_delphi_utils_python/.bumpversion.cfg index 8ce49f20b..988c45f97 100644 --- a/_delphi_utils_python/.bumpversion.cfg +++ b/_delphi_utils_python/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.3.0 +current_version = 0.3.1 commit = True message = chore: bump delphi_utils to {new_version} tag = False diff --git a/_delphi_utils_python/data_proc/geomap/README.md b/_delphi_utils_python/data_proc/geomap/README.md index 864946cb3..84fdbefb2 100644 --- a/_delphi_utils_python/data_proc/geomap/README.md +++ b/_delphi_utils_python/data_proc/geomap/README.md @@ -17,11 +17,11 @@ You can see consistency checks and diffs with old sources in ./consistency_check We support the following geocodes. -- The ZIP code and the FIPS code are the most granular geocodes we support. +- The ZIP code and the FIPS code are the most granular geocodes we support. - The [ZIP code](https://en.wikipedia.org/wiki/ZIP_Code) is a US postal code used by the USPS and the [FIPS code](https://en.wikipedia.org/wiki/FIPS_county_code) is an identifier for US counties and other associated territories. The ZIP code is five digit code (with leading zeros). - The FIPS code is a five digit code (with leading zeros), where the first two digits are a two-digit state code and the last three are a three-digit county code (see this [US Census Bureau page](https://www.census.gov/library/reference/code-lists/ansi.html) for detailed information). - The Metropolitan Statistical Area (MSA) code refers to regions around cities (these are sometimes referred to as CBSA codes). More information on these can be found at the [US Census Bureau](https://www.census.gov/programs-surveys/metro-micro/about.html). - - We are reserving 10001-10099 for states codes of the form 100XX where XX is the FIPS code for the state (the current smallest CBSA is 10100). In the case that the CBSA codes change then it should be verified that these are not used. + - We are reserving 10001-10099 for states codes of the form 100XX where XX is the FIPS code for the state (the current smallest CBSA is 10100). In the case that the CBSA codes change then it should be verified that these are not used. - State codes are a series of equivalent identifiers for US state. They include the state name, the state number (state_id), and the state two-letter abbreviation (state_code). The state number is the state FIPS code. See [here](https://en.wikipedia.org/wiki/List_of_U.S._state_and_territory_abbreviations) for more. - The Hospital Referral Region (HRR) and the Hospital Service Area (HSA). More information [here](https://www.dartmouthatlas.org/covid-19/hrr-mapping/). - The JHU signal contains its own geographic identifier, labeled the UID. Documentation is provided at [their repo](https://github.com/CSSEGISandData/COVID-19/tree/master/csse_covid_19_data#uid-lookup-table-logic). Its FIPS codes depart in some special cases, so we produce manual changes listed below. @@ -30,7 +30,7 @@ We support the following geocodes. The source files are requested from a government URL when `geo_data_proc.py` is run (see the top of said script for the URLs). Below we describe the locations to find updated versions of the source files, if they are ever needed. -- ZIP -> FIPS (county) population tables available from [US Census](https://www.census.gov/geographies/reference-files/time-series/geo/relationship-files.html#par_textimage_674173622). This file contains the population of the intersections between ZIP and FIPS regions, allowing the creation of a population-weighted transform between the two. +- ZIP -> FIPS (county) population tables available from [US Census](https://www.census.gov/geographies/reference-files/time-series/geo/relationship-files.html#par_textimage_674173622). This file contains the population of the intersections between ZIP and FIPS regions, allowing the creation of a population-weighted transform between the two. As of 4 February 2022, this source did not include population information for 24 ZIPs that appear in our indicators. We have added those values manually using information available from the [zipdatamaps website](www.zipdatamaps.com). - ZIP -> HRR -> HSA crosswalk file comes from the 2018 version at the [Dartmouth Atlas Project](https://atlasdata.dartmouth.edu/static/supp_research_data). - FIPS -> MSA crosswalk file comes from the September 2018 version of the delineation files at the [US Census Bureau](https://www.census.gov/geographies/reference-files/time-series/demo/metro-micro/delineation-files.html). - State Code -> State ID -> State Name comes from the ANSI standard at the [US Census](https://www.census.gov/library/reference/code-lists/ansi.html#par_textimage_3). The first two digits of a FIPS codes should match the state code here. @@ -60,6 +60,6 @@ The rest of the crosswalk tables are derived from the mappings above. We provide - MSA tables from March 2020 [here](https://www.census.gov/geographies/reference-files/time-series/demo/metro-micro/delineation-files.html). This file seems to differ in a few fips codes from the source for the 02_20_uszip file which Jingjing constructed. There are at least 10 additional fips in 03_20_msa that are not in the uszip file, and one of the msa codes seems to be incorrect: 49020 (a google search confirms that it is incorrect in uszip and correct in the census data). - MSA tables from 2019 [here](https://apps.bea.gov/regional/docs/msalist.cfm) -## Notes +## Notes - The NAs in the coding currently zero-fills. diff --git a/_delphi_utils_python/data_proc/geomap/geo_data_proc.py b/_delphi_utils_python/data_proc/geomap/geo_data_proc.py index 8ca4057e1..45e4e4ee3 100755 --- a/_delphi_utils_python/data_proc/geomap/geo_data_proc.py +++ b/_delphi_utils_python/data_proc/geomap/geo_data_proc.py @@ -32,6 +32,7 @@ FIPS_POPULATION_URL = f"https://www2.census.gov/programs-surveys/popest/datasets/2010-{YEAR}/counties/totals/co-est{YEAR}-alldata.csv" FIPS_PUERTO_RICO_POPULATION_URL = "https://www2.census.gov/geo/docs/maps-data/data/rel/zcta_county_rel_10.txt?" STATE_HHS_FILE = "hhs.txt" +ZIP_POP_MISSING_FILE = "zip_pop_filling.csv" # Out files FIPS_STATE_OUT_FILENAME = "fips_state_table.csv" @@ -181,6 +182,7 @@ def create_jhu_uid_fips_crosswalk(): ] ) + jhu_df = pd.read_csv(JHU_FIPS_URL, dtype={"UID": str, "FIPS": str}).query("Country_Region == 'US'") jhu_df = jhu_df.rename(columns={"UID": "jhu_uid", "FIPS": "fips"}).dropna(subset=["fips"]) @@ -336,6 +338,7 @@ def create_hhs_population_table(): state_pop = pd.read_csv(join(OUTPUT_DIR, STATE_POPULATION_OUT_FILENAME), dtype={"state_code": str, "hhs": int}, usecols=["state_code", "pop"]) state_hhs = pd.read_csv(join(OUTPUT_DIR, STATE_HHS_OUT_FILENAME), dtype=str) hhs_pop = state_pop.merge(state_hhs, on="state_code").groupby("hhs", as_index=False).sum() + hhs_pop.sort_values("hhs").to_csv(join(OUTPUT_DIR, HHS_POPULATION_OUT_FILENAME), index=False) @@ -363,6 +366,18 @@ def derive_zip_population_table(): df = census_pop.merge(fz_df, on="fips", how="left") df["pop"] = df["pop"].multiply(df["weight"], axis=0) df = df.drop(columns=["fips", "weight"]).groupby("zip").sum().dropna().reset_index() + + ## loading populatoin of some zips- #Issue 0648 + zip_pop_missing = pd.read_csv( + ZIP_POP_MISSING_FILE,sep=",", + dtype={"zip":str,"pop":np.int32} + ) + ## cheking if each zip still missing, and concatenating if True + for x_zip in zip_pop_missing['zip']: + if x_zip not in df['zip']: + df = pd.concat([df, zip_pop_missing[zip_pop_missing['zip'] == x_zip]], + ignore_index=True) + df["pop"] = df["pop"].astype(int) df.sort_values("zip").to_csv(join(OUTPUT_DIR, ZIP_POPULATION_OUT_FILENAME), index=False) diff --git a/_delphi_utils_python/data_proc/geomap/zip_pop_filling.csv b/_delphi_utils_python/data_proc/geomap/zip_pop_filling.csv new file mode 100644 index 000000000..fef98c92e --- /dev/null +++ b/_delphi_utils_python/data_proc/geomap/zip_pop_filling.csv @@ -0,0 +1,25 @@ +zip,pop +57756,1126 +57764,1923 +57770,5271 +57772,2048 +57794,644 +99554,677 +99563,938 +99566,192 +99573,1115 +99574,2348 +99581,762 +99585,417 +99586,605 +99604,1093 +99620,577 +99632,813 +99650,568 +99657,329 +99658,616 +99662,480 +99666,189 +99677,88 +99686,4005 +99693,248 diff --git a/_delphi_utils_python/delphi_utils/__init__.py b/_delphi_utils_python/delphi_utils/__init__.py index 898e40e4b..6ea299174 100644 --- a/_delphi_utils_python/delphi_utils/__init__.py +++ b/_delphi_utils_python/delphi_utils/__init__.py @@ -15,4 +15,4 @@ from .nancodes import Nans from .weekday import Weekday -__version__ = "0.3.0" +__version__ = "0.3.1" diff --git a/_delphi_utils_python/delphi_utils/data/2019/zip_pop.csv b/_delphi_utils_python/delphi_utils/data/2019/zip_pop.csv index 5c95ac758..4bb1b1e6c 100644 --- a/_delphi_utils_python/delphi_utils/data/2019/zip_pop.csv +++ b/_delphi_utils_python/delphi_utils/data/2019/zip_pop.csv @@ -19549,15 +19549,19 @@ zip,pop 57752,317 57754,4067 57755,119 +57756,1126 57758,219 57759,585 57760,1395 57761,1360 57762,547 57763,266 +57764,1923 57766,224 57767,167 57769,3915 +57770,5271 +57772,2048 57773,145 57775,251 57776,15 @@ -19572,6 +19576,7 @@ zip,pop 57791,213 57792,143 57793,2061 +57794,644 57799,707 58001,54 58002,27 @@ -32756,21 +32761,26 @@ zip,pop 99551,677 99552,373 99553,1092 +99554,677 99555,224 99556,2659 99557,784 99558,79 99559,8248 99561,451 +99563,938 99564,88 99565,76 99566,183 +99566,192 99567,9090 99568,295 99569,64 99571,170 99572,313 +99573,1115 99573,1064 +99574,2348 99574,2242 99575,113 99576,2640 @@ -32778,7 +32788,10 @@ zip,pop 99578,319 99579,106 99580,116 +99581,762 99583,37 +99585,417 +99586,605 99586,577 99587,2220 99588,958 @@ -32787,6 +32800,7 @@ zip,pop 99591,107 99602,166 99603,10427 +99604,1093 99605,223 99606,457 99607,233 @@ -32797,6 +32811,7 @@ zip,pop 99613,372 99614,690 99615,12347 +99620,577 99621,781 99622,346 99624,86 @@ -32806,6 +32821,7 @@ zip,pop 99628,449 99630,206 99631,241 +99632,813 99633,456 99634,382 99636,517 @@ -32820,18 +32836,23 @@ zip,pop 99647,42 99648,110 99649,78 +99650,568 99651,65 99652,4506 99653,155 99654,63494 99655,723 99656,27 +99657,329 +99658,616 99659,422 99660,503 99661,1040 +99662,480 99663,438 99664,5226 99665,77 +99666,189 99667,79 99668,92 99669,15038 @@ -32840,6 +32861,7 @@ zip,pop 99672,3925 99674,1757 99676,1881 +99677,88 99677,84 99678,903 99679,403 @@ -32850,11 +32872,13 @@ zip,pop 99684,725 99685,4438 99686,3824 +99686,4005 99688,3358 99689,579 99690,302 99691,88 99692,159 +99693,248 99693,236 99694,1840 99695,6 diff --git a/_delphi_utils_python/setup.py b/_delphi_utils_python/setup.py index 779ef0fd4..cfbafc9f0 100644 --- a/_delphi_utils_python/setup.py +++ b/_delphi_utils_python/setup.py @@ -26,7 +26,7 @@ setup( name="delphi_utils", - version="0.3.0", + version="0.3.1", description="Shared Utility Functions for Indicators", long_description=long_description, long_description_content_type="text/markdown", diff --git a/ansible/templates/quidel_covidtest-params-prod.json.j2 b/ansible/templates/quidel_covidtest-params-prod.json.j2 index 35119ad02..fe605206b 100644 --- a/ansible/templates/quidel_covidtest-params-prod.json.j2 +++ b/ansible/templates/quidel_covidtest-params-prod.json.j2 @@ -48,6 +48,15 @@ ] } }, + "archive": { + "aws_credentials": { + "aws_access_key_id": "{{ delphi_aws_access_key_id }}", + "aws_secret_access_key": "{{ delphi_aws_secret_access_key }}" + }, + "bucket_name": "delphi-covidcast-indicator-output", + "cache_dir": "./archivediffer_cache", + "indicator_prefix": "quidel" + }, "delivery": { "delivery_dir": "/common/covidcast/receiving/quidel" } diff --git a/quidel_covidtest/.pylintrc b/quidel_covidtest/.pylintrc index 58c6edbba..854cf38d2 100644 --- a/quidel_covidtest/.pylintrc +++ b/quidel_covidtest/.pylintrc @@ -4,6 +4,7 @@ disable=logging-format-interpolation, too-many-locals, too-many-arguments, + too-many-branches, # Allow pytest functions to be part of a class. no-self-use, # Allow pytest classes to have one test. diff --git a/quidel_covidtest/delphi_quidel_covidtest/constants.py b/quidel_covidtest/delphi_quidel_covidtest/constants.py index abe88c7b1..8e4d37cb2 100644 --- a/quidel_covidtest/delphi_quidel_covidtest/constants.py +++ b/quidel_covidtest/delphi_quidel_covidtest/constants.py @@ -3,7 +3,7 @@ MIN_OBS = 50 # minimum number of observations in order to compute a proportion. POOL_DAYS = 7 # number of days in the past (including today) to pool over END_FROM_TODAY_MINUS = 5 # report data until - X days -# Signal names +# Signal Types SMOOTHED_POSITIVE = "covid_ag_smoothed_pct_positive" RAW_POSITIVE = "covid_ag_raw_pct_positive" SMOOTHED_TEST_PER_DEVICE = "covid_ag_smoothed_test_per_device" @@ -22,6 +22,7 @@ HRR, ] +# state should be last one NONPARENT_GEO_RESOLUTIONS = [ HHS, NATION, @@ -39,3 +40,12 @@ # SMOOTHED_TEST_PER_DEVICE: (True, True), # RAW_TEST_PER_DEVICE: (True, False) } +AGE_GROUPS = [ + "total", + "age_0_4", + "age_5_17", + "age_18_49", + "age_50_64", + "age_65plus", + "age_0_17", +] diff --git a/quidel_covidtest/delphi_quidel_covidtest/data_tools.py b/quidel_covidtest/delphi_quidel_covidtest/data_tools.py index f89a353ed..e8958ecfd 100644 --- a/quidel_covidtest/delphi_quidel_covidtest/data_tools.py +++ b/quidel_covidtest/delphi_quidel_covidtest/data_tools.py @@ -67,15 +67,14 @@ def _slide_window_sum(arr, k): sarr = np.convolve(temp, np.ones(k, dtype=int), 'valid') return sarr - def _geographical_pooling(tpooled_tests, tpooled_ptests, min_obs): """ Determine how many samples from the parent geography must be borrowed. - If there are no samples available in the parent, the borrow_prop is 0. If - the parent does not have enough samples, we return a borrow_prop of 1, and - the fact that the pooled samples are insufficient are handled in the - statistic fitting step. + If there are no samples available in the parent, the borrow_prop is 0. + If the parent does not have enough samples, we return a borrow_prop of 1. + No more samples borrowed from the parent compared to the number of samples + we currently have. Args: tpooled_tests: np.ndarray[float] @@ -93,10 +92,12 @@ def _geographical_pooling(tpooled_tests, tpooled_ptests, min_obs): """ if (np.any(np.isnan(tpooled_tests)) or np.any(np.isnan(tpooled_ptests))): raise ValueError('[parent] tests should be non-negative ' - 'with no np.nan') + 'with no np.nan') # STEP 1: "TOP UP" USING PARENT LOCATION # Number of observations we need to borrow to "top up" + # Can't borrow more than total no. observations. borrow_tests = np.maximum(min_obs - tpooled_tests, 0) + borrow_tests = np.minimum(borrow_tests, tpooled_tests) # There are many cases (a, b > 0): # Case 1: a / b => no problem # Case 2: a / 0 => np.inf => borrow_prop becomes 1 @@ -108,13 +109,14 @@ def _geographical_pooling(tpooled_tests, tpooled_ptests, min_obs): with np.errstate(divide='ignore', invalid='ignore'): borrow_prop = borrow_tests / tpooled_ptests # If there's nothing to borrow, then ya can't borrow - borrow_prop[np.isnan(borrow_prop)] = 0 - # Can't borrow more than total no. observations. + borrow_prop[(np.isnan(borrow_prop)) + | (tpooled_tests == 0) + | (tpooled_ptests == 0)] = 0 + # Can't borrow more than total no. observations in the parent state # Relies on the fact that np.inf > 1 borrow_prop[borrow_prop > 1] = 1 return borrow_prop - def raw_positive_prop(positives, tests, min_obs): """ Calculate the proportion of positive tests without any temporal smoothing. diff --git a/quidel_covidtest/delphi_quidel_covidtest/generate_sensor.py b/quidel_covidtest/delphi_quidel_covidtest/generate_sensor.py index ba6b443c6..5f44a519b 100644 --- a/quidel_covidtest/delphi_quidel_covidtest/generate_sensor.py +++ b/quidel_covidtest/delphi_quidel_covidtest/generate_sensor.py @@ -6,11 +6,12 @@ smoothed_tests_per_device, raw_tests_per_device, remove_null_samples) +from .geo_maps import add_megacounties -MIN_OBS = 50 # minimum number of observations in order to compute a proportion. -POOL_DAYS = 7 +from .constants import (MIN_OBS, POOL_DAYS) -def generate_sensor_for_nonparent_geo(state_groups, res_key, smooth, device, first_date, last_date): +def generate_sensor_for_nonparent_geo(state_groups, res_key, smooth, device, + first_date, last_date, suffix): """ Fit over geo resolutions that don't use a parent state (nation/hhs/state). @@ -21,6 +22,8 @@ def generate_sensor_for_nonparent_geo(state_groups, res_key, smooth, device, fir Consider raw or smooth device: bool Consider test_per_device or pct_positive + suffix: str + Indicate the age group Returns: df: pd.DataFrame """ @@ -35,27 +38,27 @@ def generate_sensor_for_nonparent_geo(state_groups, res_key, smooth, device, fir # smoothed test per device if device & smooth: stat, se, sample_size = smoothed_tests_per_device( - devices=state_group["numUniqueDevices"].values, - tests=state_group['totalTest'].values, + devices=state_group[f"numUniqueDevices_{suffix}"].values, + tests=state_group[f'totalTest_{suffix}'].values, min_obs=MIN_OBS, pool_days=POOL_DAYS) # raw test per device elif device & (not smooth): stat, se, sample_size = raw_tests_per_device( - devices=state_group["numUniqueDevices"].values, - tests=state_group['totalTest'].values, + devices=state_group[f"numUniqueDevices_{suffix}"].values, + tests=state_group[f'totalTest_{suffix}'].values, min_obs=MIN_OBS) # smoothed pct positive elif (not device) & smooth: stat, se, sample_size = smoothed_positive_prop( - tests=state_group['totalTest'].values, - positives=state_group['positiveTest'].values, + tests=state_group[f'totalTest_{suffix}'].values, + positives=state_group[f'positiveTest_{suffix}'].values, min_obs=MIN_OBS, pool_days=POOL_DAYS) stat = stat * 100 # raw pct positive else: stat, se, sample_size = raw_positive_prop( - tests=state_group['totalTest'].values, - positives=state_group['positiveTest'].values, + tests=state_group[f'totalTest_{suffix}'].values, + positives=state_group[f'positiveTest_{suffix}'].values, min_obs=MIN_OBS) stat = stat * 100 @@ -68,7 +71,7 @@ def generate_sensor_for_nonparent_geo(state_groups, res_key, smooth, device, fir return remove_null_samples(state_df) def generate_sensor_for_parent_geo(state_groups, data, res_key, smooth, - device, first_date, last_date): + device, first_date, last_date, suffix): """ Fit over geo resolutions that use a parent state (county/hrr/msa). @@ -79,15 +82,16 @@ def generate_sensor_for_parent_geo(state_groups, data, res_key, smooth, Consider raw or smooth device: bool Consider test_per_device or pct_positive + suffix: str + Indicate the age group Returns: df: pd.DataFrame """ has_parent = True res_df = pd.DataFrame(columns=["geo_id", "val", "se", "sample_size"]) - res_groups = data.groupby(res_key) - loc_list = list(res_groups.groups.keys()) - for loc in loc_list: - res_group = res_groups.get_group(loc) + if res_key == "fips": # Add rest-of-state report for county level + data = add_megacounties(data, smooth) + for loc, res_group in data.groupby(res_key): parent_state = res_group['state_id'].values[0] try: parent_group = state_groups.get_group(parent_state) @@ -104,41 +108,41 @@ def generate_sensor_for_parent_geo(state_groups, data, res_key, smooth, if has_parent: if device: stat, se, sample_size = smoothed_tests_per_device( - devices=res_group["numUniqueDevices"].values, - tests=res_group['totalTest'].values, + devices=res_group[f"numUniqueDevices_{suffix}"].values, + tests=res_group[f'totalTest_{suffix}'].values, min_obs=MIN_OBS, pool_days=POOL_DAYS, - parent_devices=res_group["numUniqueDevices_parent"].values, - parent_tests=res_group["totalTest_parent"].values) + parent_devices=res_group[f"numUniqueDevices_{suffix}_parent"].values, + parent_tests=res_group[f"totalTest_{suffix}_parent"].values) else: stat, se, sample_size = smoothed_positive_prop( - tests=res_group['totalTest'].values, - positives=res_group['positiveTest'].values, + tests=res_group[f'totalTest_{suffix}'].values, + positives=res_group[f'positiveTest_{suffix}'].values, min_obs=MIN_OBS, pool_days=POOL_DAYS, - parent_tests=res_group["totalTest_parent"].values, - parent_positives=res_group['positiveTest_parent'].values) + parent_tests=res_group[f"totalTest_{suffix}_parent"].values, + parent_positives=res_group[f'positiveTest_{suffix}_parent'].values) stat = stat * 100 else: if device: stat, se, sample_size = smoothed_tests_per_device( - devices=res_group["numUniqueDevices"].values, - tests=res_group['totalTest'].values, + devices=res_group[f"numUniqueDevices_{suffix}"].values, + tests=res_group[f'totalTest_{suffix}'].values, min_obs=MIN_OBS, pool_days=POOL_DAYS) else: stat, se, sample_size = smoothed_positive_prop( - tests=res_group['totalTest'].values, - positives=res_group['positiveTest'].values, + tests=res_group[f'totalTest_{suffix}'].values, + positives=res_group[f'positiveTest_{suffix}'].values, min_obs=MIN_OBS, pool_days=POOL_DAYS) stat = stat * 100 else: if device: stat, se, sample_size = raw_tests_per_device( - devices=res_group["numUniqueDevices"].values, - tests=res_group['totalTest'].values, + devices=res_group[f"numUniqueDevices_{suffix}"].values, + tests=res_group[f'totalTest_{suffix}'].values, min_obs=MIN_OBS) else: stat, se, sample_size = raw_positive_prop( - tests=res_group['totalTest'].values, - positives=res_group['positiveTest'].values, + tests=res_group[f'totalTest_{suffix}'].values, + positives=res_group[f'positiveTest_{suffix}'].values, min_obs=MIN_OBS) stat = stat * 100 diff --git a/quidel_covidtest/delphi_quidel_covidtest/geo_maps.py b/quidel_covidtest/delphi_quidel_covidtest/geo_maps.py index 7ebdcc834..9cefc0f9e 100644 --- a/quidel_covidtest/delphi_quidel_covidtest/geo_maps.py +++ b/quidel_covidtest/delphi_quidel_covidtest/geo_maps.py @@ -1,7 +1,13 @@ """Contains geographic mapping tools.""" +from itertools import product +from functools import reduce + +import pandas as pd + from delphi_utils import GeoMapper +from .constants import (AGE_GROUPS, MIN_OBS) -DATA_COLS = ['totalTest', 'numUniqueDevices', 'positiveTest', "population"] +DATA_COLS = ['totalTest', 'numUniqueDevices', 'positiveTest'] GMPR = GeoMapper() # Use geo utils GEO_KEY_DICT = { "county": "fips", @@ -12,7 +18,6 @@ "hhs": "hhs" } - def geo_map(geo_res, df): """Map a geocode to a new value.""" data = df.copy() @@ -20,13 +25,45 @@ def geo_map(geo_res, df): # Add population for each zipcode data = GMPR.add_population_column(data, "zip") # zip -> geo_res - data = GMPR.replace_geocode(data, "zip", geo_key, data_cols=DATA_COLS) + data_cols = ["population"] + for col, agegroup in product(DATA_COLS, AGE_GROUPS): + data_cols.append("_".join([col, agegroup])) + + data = GMPR.replace_geocode( + data, from_code="zip", new_code=geo_key, date_col = "timestamp", + data_cols=data_cols) if geo_res in ["state", "hhs", "nation"]: return data, geo_key # Add parent state data = add_parent_state(data, geo_res, geo_key) return data, geo_key +def add_megacounties(data, smooth=False): + """Add megacounties to county level report.""" + assert "fips" in data.columns # Make sure the data is at county level + + # For raw signals, the threshold is MIN_OBS + # For smoothed signals, the threshold is MIN_OBS/2 + if smooth: + threshold_visits = MIN_OBS/2 + else: + threshold_visits = MIN_OBS + pdList = [] + for agegroup in AGE_GROUPS: + data_cols = [f"{col}_{agegroup}" for col in DATA_COLS] + df = GMPR.fips_to_megacounty(data[data_cols + ["timestamp", "fips"]], + threshold_visits, 1, fips_col="fips", + thr_col=f"totalTest_{agegroup}", + date_col="timestamp") + df.rename({"megafips": "fips"}, axis=1, inplace=True) + megacounties = df[df.fips.str.endswith("000")] + pdList.append(megacounties) + mega_df = reduce(lambda x, y: pd.merge( + x, y, on = ["timestamp", "fips"]), pdList) + mega_df = GMPR.add_geocode(mega_df, from_code="fips", new_code="state_id", + from_col="fips", new_col="state_id") + + return pd.concat([data, mega_df]) def add_parent_state(data, geo_res, geo_key): """ diff --git a/quidel_covidtest/delphi_quidel_covidtest/pull.py b/quidel_covidtest/delphi_quidel_covidtest/pull.py index 3efa9ed23..ab397ce47 100644 --- a/quidel_covidtest/delphi_quidel_covidtest/pull.py +++ b/quidel_covidtest/delphi_quidel_covidtest/pull.py @@ -8,6 +8,8 @@ import pandas as pd import numpy as np +from .constants import AGE_GROUPS + def get_from_s3(start_date, end_date, bucket, logger): """ Get raw data from aws s3 bucket. @@ -163,21 +165,21 @@ def preprocess_new_data(start_date, end_date, params, test_mode, logger): overall_pos = df[df["OverallResult"] == "positive"].groupby( by=["timestamp", "zip"], as_index=False)['OverallResult'].count() - overall_pos["positiveTest"] = overall_pos["OverallResult"] + overall_pos["positiveTest_total"] = overall_pos["OverallResult"] overall_pos.drop(labels="OverallResult", axis="columns", inplace=True) # Compute overallTotal overall_total = df.groupby( by=["timestamp", "zip"], as_index=False)['OverallResult'].count() - overall_total["totalTest"] = overall_total["OverallResult"] + overall_total["totalTest_total"] = overall_total["OverallResult"] overall_total.drop(labels="OverallResult", axis="columns", inplace=True) # Compute numUniqueDevices numUniqueDevices = df.groupby( by=["timestamp", "zip"], as_index=False)["SofiaSerNum"].agg({"SofiaSerNum": "nunique"}).rename( - columns={"SofiaSerNum": "numUniqueDevices"} + columns={"SofiaSerNum": "numUniqueDevices_total"} ) df_merged = overall_total.merge( @@ -186,6 +188,55 @@ def preprocess_new_data(start_date, end_date, params, test_mode, logger): overall_pos, on=["timestamp", "zip"], how="left" ).fillna(0).drop_duplicates() + # Compute Summary info for age groups + df["PatientAge"] = df["PatientAge"].fillna(-1) + df.loc[df["PatientAge"] == "<1", "PatientAge"] = 0.5 + df.loc[df["PatientAge"] == ">85", "PatientAge"] = 100 + df["PatientAge"] = df["PatientAge"] .astype(float) + + # Should match the suffixes of signal names + df["label"] = None + df.loc[df["PatientAge"] < 5, "label"] = "age_0_4" + df.loc[((df["PatientAge"] >= 5)) & (df["PatientAge"] < 18), "label"] = "age_5_17" + df.loc[((df["PatientAge"] >= 18)) & (df["PatientAge"] < 50), "label"] = "age_18_49" + df.loc[((df["PatientAge"] >= 50)) & (df["PatientAge"] < 65), "label"] = "age_50_64" + df.loc[(df["PatientAge"] >= 65), "label"] = "age_65plus" + df.loc[df["PatientAge"] == -1, "label"] = "NA" + + for agegroup in AGE_GROUPS[1:]: # Exclude total + if agegroup == "age_0_17": + ages = ["age_0_4", "age_5_17"] + else: + ages = [agegroup] + # Compute overallPositive + group_pos = df.loc[(df["OverallResult"] == "positive") + & (df["label"].isin(ages))].groupby( + by=["timestamp", "zip"], + as_index=False)['OverallResult'].count() + group_pos[f"positiveTest_{agegroup}"] = group_pos["OverallResult"] + group_pos.drop(labels="OverallResult", axis="columns", inplace=True) + + # Compute overallTotal + group_total = df.loc[df["label"].isin(ages)].groupby( + by=["timestamp", "zip"], + as_index=False)['OverallResult'].count() + group_total[f"totalTest_{agegroup}"] = group_total["OverallResult"] + group_total.drop(labels="OverallResult", axis="columns", inplace=True) + + # Compute numUniqueDevices + group_numUniqueDevices = df.loc[df["label"].isin(ages)].groupby( + by=["timestamp", "zip"], + as_index=False)["SofiaSerNum"].agg({"SofiaSerNum": "nunique"}).rename( + columns={"SofiaSerNum": f"numUniqueDevices_{agegroup}"} + ) + + df_merged = df_merged.merge( + group_numUniqueDevices, on=["timestamp", "zip"], how="left" + ).merge( + group_pos, on=["timestamp", "zip"], how="left" + ).merge( + group_total, on=["timestamp", "zip"], how="left" + ).fillna(0).drop_duplicates() return df_merged, time_flag @@ -295,7 +346,7 @@ def check_export_start_date(export_start_date, export_end_date, export_start_date = datetime(2020, 5, 26) else: export_start_date = datetime.strptime(export_start_date, '%Y-%m-%d') - # Only export data from -45 days to -5 days + # Only export data from -50 days to -5 days if (export_end_date - export_start_date).days > export_day_range: export_start_date = export_end_date - timedelta(days=export_day_range) diff --git a/quidel_covidtest/delphi_quidel_covidtest/run.py b/quidel_covidtest/delphi_quidel_covidtest/run.py index 5f084440c..fb1a69ac2 100644 --- a/quidel_covidtest/delphi_quidel_covidtest/run.py +++ b/quidel_covidtest/delphi_quidel_covidtest/run.py @@ -18,7 +18,8 @@ from .constants import (END_FROM_TODAY_MINUS, SMOOTHED_POSITIVE, RAW_POSITIVE, SMOOTHED_TEST_PER_DEVICE, RAW_TEST_PER_DEVICE, - PARENT_GEO_RESOLUTIONS, SENSORS, SMOOTHERS, NONPARENT_GEO_RESOLUTIONS) + PARENT_GEO_RESOLUTIONS, SENSORS, SMOOTHERS, NONPARENT_GEO_RESOLUTIONS, + AGE_GROUPS) from .generate_sensor import generate_sensor_for_parent_geo, generate_sensor_for_nonparent_geo from .geo_maps import geo_map from .pull import (pull_quidel_covidtest, @@ -40,6 +41,20 @@ def log_exit(start_time, stats, logger): max_lag_in_days = max_lag_in_days, oldest_final_export_date = formatted_min_max_date) +def get_smooth_info(sensors, _SMOOTHERS): + """Get smooth info from SMOOTHERS.""" + smoothers = _SMOOTHERS.copy() + for sensor in sensors: + if sensor.endswith(SMOOTHED_POSITIVE): + smoothers[sensor] = smoothers.pop(SMOOTHED_POSITIVE) + elif sensor.endswith(RAW_POSITIVE): + smoothers[sensor] = smoothers.pop(RAW_POSITIVE) + elif sensor.endswith(SMOOTHED_TEST_PER_DEVICE): + smoothers[sensor] = smoothers.pop(SMOOTHED_TEST_PER_DEVICE) + else: + smoothers[sensor] = smoothers.pop(RAW_TEST_PER_DEVICE) + return smoothers + def run_module(params: Dict[str, Any]): """Run the quidel_covidtest indicator. @@ -80,65 +95,68 @@ def run_module(params: Dict[str, Any]): if _end_date is None: logger.info("The data is up-to-date. Currently, no new data to be ingested.") return - export_end_date = check_export_end_date(export_end_date, _end_date, - END_FROM_TODAY_MINUS) - export_start_date = check_export_start_date(export_start_date, - export_end_date, export_day_range) + export_end_date = check_export_end_date( + export_end_date, _end_date, END_FROM_TODAY_MINUS) + export_start_date = check_export_start_date( + export_start_date, export_end_date, export_day_range) first_date, last_date = df["timestamp"].min(), df["timestamp"].max() - # State Level data = df.copy() # Add prefix, if required sensors = add_prefix(SENSORS, wip_signal=params["indicator"]["wip_signal"], prefix="wip_") - smoothers = SMOOTHERS.copy() + smoothers = get_smooth_info(sensors, SMOOTHERS) for geo_res in NONPARENT_GEO_RESOLUTIONS: geo_data, res_key = geo_map(geo_res, data) geo_groups = geo_data.groupby(res_key) - for sensor in sensors: - logger.info("Generating signal and exporting to CSV", - geo_res=geo_res, - sensor=sensor) - if sensor.endswith(SMOOTHED_POSITIVE): - smoothers[sensor] = smoothers.pop(SMOOTHED_POSITIVE) - elif sensor.endswith(RAW_POSITIVE): - smoothers[sensor] = smoothers.pop(RAW_POSITIVE) - elif sensor.endswith(SMOOTHED_TEST_PER_DEVICE): - smoothers[sensor] = smoothers.pop(SMOOTHED_TEST_PER_DEVICE) - else: - smoothers[sensor] = smoothers.pop(RAW_TEST_PER_DEVICE) - state_df = generate_sensor_for_nonparent_geo( - geo_groups, res_key, smooth=smoothers[sensor][1], - device=smoothers[sensor][0], first_date=first_date, - last_date=last_date) - dates = create_export_csv( - state_df, - geo_res=geo_res, - sensor=sensor, - export_dir=export_dir, - start_date=export_start_date, - end_date=export_end_date) - if len(dates) > 0: - stats.append((max(dates), len(dates))) - + for agegroup in AGE_GROUPS: + for sensor in sensors: + if agegroup == "total": + sensor_name = sensor + else: + sensor_name = "_".join([sensor, agegroup]) + logger.info("Generating signal and exporting to CSV", + geo_res=geo_res, + sensor=sensor_name) + state_df = generate_sensor_for_nonparent_geo( + geo_groups, res_key, smooth=smoothers[sensor][1], + device=smoothers[sensor][0], first_date=first_date, + last_date=last_date, suffix=agegroup) + dates = create_export_csv( + state_df, + geo_res=geo_res, + sensor=sensor_name, + export_dir=export_dir, + start_date=export_start_date, + end_date=export_end_date) + if len(dates) > 0: + stats.append((max(dates), len(dates))) + assert geo_res == "state" # Make sure geo_groups is for state level # County/HRR/MSA level for geo_res in PARENT_GEO_RESOLUTIONS: geo_data, res_key = geo_map(geo_res, data) - for sensor in sensors: - logger.info("Generating signal and exporting to CSV", - geo_res=geo_res, - sensor=sensor) - res_df = generate_sensor_for_parent_geo( - geo_groups, geo_data, res_key, smooth=smoothers[sensor][1], - device=smoothers[sensor][0], first_date=first_date, - last_date=last_date) - dates = create_export_csv(res_df, geo_res=geo_res, sensor=sensor, export_dir=export_dir, - start_date=export_start_date, end_date=export_end_date, - remove_null_samples=True) - if len(dates) > 0: - stats.append((max(dates), len(dates))) + for agegroup in AGE_GROUPS: + for sensor in sensors: + if agegroup == "total": + sensor_name = sensor + else: + sensor_name = "_".join([sensor, agegroup]) + logger.info("Generating signal and exporting to CSV", + geo_res=geo_res, + sensor=sensor_name) + res_df = generate_sensor_for_parent_geo( + geo_groups, geo_data, res_key, smooth=smoothers[sensor][1], + device=smoothers[sensor][0], first_date=first_date, + last_date=last_date, suffix=agegroup) + dates = create_export_csv(res_df, geo_res=geo_res, + sensor=sensor_name, export_dir=export_dir, + start_date=export_start_date, + end_date=export_end_date, + remove_null_samples=True) + if len(dates) > 0: + stats.append((max(dates), len(dates))) # Export the cache file if the pipeline runs successfully. # Otherwise, don't update the cache file diff --git a/quidel_covidtest/params.json.template b/quidel_covidtest/params.json.template index a2996b032..e96d2ccbc 100644 --- a/quidel_covidtest/params.json.template +++ b/quidel_covidtest/params.json.template @@ -20,6 +20,15 @@ "wip_signal": [""], "test_mode": false }, + "archive": { + "aws_credentials": { + "aws_access_key_id": "{{ delphi_aws_access_key_id }}", + "aws_secret_access_key": "{{ delphi_aws_secret_access_key }}" + }, + "bucket_name": "delphi-covidcast-indicator-output", + "cache_dir": "./archivediffer_cache", + "indicator_prefix": "quidel" + }, "validation": { "common": { "data_source": "quidel", diff --git a/quidel_covidtest/tests/test_data/msa_data.csv b/quidel_covidtest/tests/test_data/msa_data.csv index 39593b937..8ff61dd69 100644 --- a/quidel_covidtest/tests/test_data/msa_data.csv +++ b/quidel_covidtest/tests/test_data/msa_data.csv @@ -1,4 +1,4 @@ -timestamp,cbsa_id,state_id,totalTest,numUniqueDevices,positiveTest +timestamp,cbsa_id,state_id,totalTest_total,numUniqueDevices_total,positiveTest_total 2020-06-14,10580,ny,1,1,0.0 2020-06-14,11100,tx,5,2,0.0 2020-06-14,12020,ga,8,1,0.0 diff --git a/quidel_covidtest/tests/test_data/state_data.csv b/quidel_covidtest/tests/test_data/state_data.csv index 6326c60d3..38e22141c 100644 --- a/quidel_covidtest/tests/test_data/state_data.csv +++ b/quidel_covidtest/tests/test_data/state_data.csv @@ -1,4 +1,4 @@ -timestamp,state_id,totalTest,numUniqueDevices,positiveTest +timestamp,state_id,totalTest_total,numUniqueDevices_total,positiveTest_total 2020-06-14,al,6,2,1.0 2020-06-14,ar,4,1,1.0 2020-06-14,ca,53,6,2.0 diff --git a/quidel_covidtest/tests/test_data/test_data.csv b/quidel_covidtest/tests/test_data/test_data.csv index 8b9d18c4f..e93a109d4 100644 --- a/quidel_covidtest/tests/test_data/test_data.csv +++ b/quidel_covidtest/tests/test_data/test_data.csv @@ -1,425 +1,538 @@ SofiaSerNum,TestDate,Facility,City,State,Zip,PatientAge,Result1,Result2,OverallResult,County,FacilityType,Assay,SCO1,SCO2,CLN,CSN,InstrType,StorageDate,ResultId,SarsTestNumber -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,invalid,,invalid,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -1,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -3,7/18/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Santa Rosa Beach,FL,32459,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,invalid,,invalid,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -1,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -2,7/19/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,positive,,positive,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,positive,,positive,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,positive,,positive,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,positive,,positive,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,positive,,positive,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,positive,,positive,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,positive,,positive,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,positive,,positive,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/19/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/20/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/20/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/20/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/20/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/20/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/20/20,,Baltimore,MD,21229,,positive,,positive,,,,,,,,,8/17/20,, -3,7/20/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/20/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/20/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -3,7/20/20,,Baltimore,MD,21229,,negative,,negative,,,,,,,,,8/17/20,, -4,7/20/20,,McLean,VA,22101,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,positive,,positive,,,,,,,,,8/17/20,, -5,7/20/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/21/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -5,7/21/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -6,7/22/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -6,7/22/20,,Tampa,FL,33625,,positive,,positive,,,,,,,,,8/17/20,, -6,7/22/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -6,7/22/20,,Tampa,FL,33625,,positive,,positive,,,,,,,,,8/17/20,, -6,7/22/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -6,7/22/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -6,7/22/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -6,7/22/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -7,7/22/20,,Columbia,SC,29229,,negative,,negative,,,,,,,,,8/17/20,, -8,7/22/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -8,7/22/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -8,7/22/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -8,7/22/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -8,7/22/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -8,7/22/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -8,7/22/20,,Lorton,VA,22079,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,positive,,positive,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,positive,,positive,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,positive,,positive,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,positive,,positive,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,positive,,positive,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, -9,7/23/20,,Tampa,FL,33625,,negative,,negative,,,,,,,,,8/17/20,, \ No newline at end of file +2,7/18/20,,Lorton,VA,22079,39,negative,,negative,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,9,negative,,negative,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,13,negative,,negative,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,77,positive,,positive,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,35,positive,,positive,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,7,negative,,negative,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,76,negative,,negative,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,28,negative,,negative,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,34,positive,,positive,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,13,negative,,negative,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,52,positive,,positive,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,34,negative,,negative,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,61,negative,,negative,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,21,negative,,negative,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,>85,positive,,positive,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,31,positive,,positive,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,67,positive,,positive,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,13,negative,,negative,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,75,positive,,positive,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/18/20,,Lorton,VA,22079,74,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,58,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,29,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,60,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,22,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,15,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,41,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,43,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,81,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,75,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,40,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,82,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,59,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,45,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,25,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,59,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,46,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,84,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,33,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,24,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,17,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,78,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,4,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,70,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,43,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,55,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,9,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,22,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,46,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,66,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,36,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,46,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,70,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,51,negative,,negative,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,5,positive,,positive,,,,,,,,,8/17/20,, +3,7/18/20,,Lorton,VA,22079,22,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24534,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24534,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24534,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24534,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24529,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24526,21,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,3,negative,,negative,,,,,,,,,8/17/20,, +1,7/18/20,,TestCity,TestState,24527,>85,positive,,positive,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,>85,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,39,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,51,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,73,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,28,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,2,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,35,positive,,positive,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,70,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,12,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,7,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,37,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,80,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,8,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,43,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,3,positive,,positive,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,70,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,57,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,38,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,29,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,1,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,2,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,>85,positive,,positive,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,22,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,40,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,59,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,70,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,25,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,74,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,>85,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,41,positive,,positive,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,71,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,29,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,54,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,39,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,15,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,48,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,77,positive,,positive,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,12,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,18,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,59,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,38,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,1,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,73,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,52,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,3,positive,,positive,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,30,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,40,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,77,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,56,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,75,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,73,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,2,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,9,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,44,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,70,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,54,positive,,positive,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,14,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,>85,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,78,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,80,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,56,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,31,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,76,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,84,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,>85,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,>85,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,69,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,79,positive,,positive,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,42,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,22,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,72,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,>85,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,54,negative,,negative,,,,,,,,,8/17/20,, +3,7/19/20,,Baltimore,MD,21229,59,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,18,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,34,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,25,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,17,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,16,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,39,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,17,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,20,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,79,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,83,invalid,,invalid,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,27,positive,,positive,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,33,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,55,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,39,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,14,positive,,positive,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,57,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,5,positive,,positive,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,45,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,74,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,44,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,83,negative,,negative,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,>85,positive,,positive,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,38,positive,,positive,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,39,positive,,positive,,,,,,,,,8/17/20,, +1,7/19/20,,Lorton,VA,22079,21,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,58,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,9,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,48,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,32,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,77,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,26,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,50,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,21,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,64,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,40,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,8,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,55,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,11,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,7,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,53,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,38,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,19,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,82,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,22,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,50,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,45,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Lorton,VA,22079,52,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,12,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,71,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,39,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,49,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,75,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,79,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,84,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,72,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,10,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,54,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,31,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,67,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,49,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,1,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,78,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,56,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,5,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,73,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,36,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,66,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,38,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,11,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,42,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,24,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,10,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,74,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,59,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,11,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,77,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,51,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,72,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,62,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,31,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,19,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,8,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,30,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,30,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,62,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,82,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,5,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,>85,negative,,negative,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,20,positive,,positive,,,,,,,,,8/17/20,, +2,7/19/20,,Santa Rosa Beach,FL,32459,8,negative,,negative,,,,,,,,,8/17/20,, +3,7/20/20,,Baltimore,MD,21229,63,negative,,negative,,,,,,,,,8/17/20,, +3,7/20/20,,Baltimore,MD,21229,80,negative,,negative,,,,,,,,,8/17/20,, +3,7/20/20,,Baltimore,MD,21229,>85,negative,,negative,,,,,,,,,8/17/20,, +3,7/20/20,,Baltimore,MD,21229,66,negative,,negative,,,,,,,,,8/17/20,, +3,7/20/20,,Baltimore,MD,21229,23,negative,,negative,,,,,,,,,8/17/20,, +3,7/20/20,,Baltimore,MD,21229,61,positive,,positive,,,,,,,,,8/17/20,, +3,7/20/20,,Baltimore,MD,21229,46,negative,,negative,,,,,,,,,8/17/20,, +3,7/20/20,,Baltimore,MD,21229,37,negative,,negative,,,,,,,,,8/17/20,, +3,7/20/20,,Baltimore,MD,21229,57,negative,,negative,,,,,,,,,8/17/20,, +3,7/20/20,,Baltimore,MD,21229,70,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,<1,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,54,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,62,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,71,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,16,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,51,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,63,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,66,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,24,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,9,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,2,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,44,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,72,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,31,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,59,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,<1,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,25,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,69,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,23,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,44,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,51,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,37,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,1,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,80,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,49,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,45,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,16,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,21,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,16,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,80,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,18,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,65,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,76,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,76,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,77,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,29,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,26,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,36,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,21,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,17,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,74,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,80,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,61,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,16,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,79,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,1,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,42,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,60,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,62,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,52,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,74,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,2,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,53,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,41,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,66,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,72,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,77,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,38,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,77,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,46,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,42,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,42,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,12,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,75,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,82,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,9,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,58,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,80,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,74,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,58,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,84,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,74,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,67,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,19,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,>85,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,54,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,68,negative,,negative,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,1,positive,,positive,,,,,,,,,8/17/20,, +5,7/20/20,,Lorton,VA,22079,48,negative,,negative,,,,,,,,,8/17/20,, +4,7/20/20,,McLean,VA,22101,73,negative,,negative,,,,,,,,,8/17/20,, +5,7/21/20,,Lorton,VA,22079,2,negative,,negative,,,,,,,,,8/17/20,, +5,7/21/20,,Lorton,VA,22079,29,negative,,negative,,,,,,,,,8/17/20,, +8,7/22/20,,Lorton,VA,22079,69,negative,,negative,,,,,,,,,8/17/20,, +8,7/22/20,,Lorton,VA,22079,76,negative,,negative,,,,,,,,,8/17/20,, +8,7/22/20,,Lorton,VA,22079,58,negative,,negative,,,,,,,,,8/17/20,, +8,7/22/20,,Lorton,VA,22079,66,negative,,negative,,,,,,,,,8/17/20,, +8,7/22/20,,Lorton,VA,22079,19,negative,,negative,,,,,,,,,8/17/20,, +8,7/22/20,,Lorton,VA,22079,70,negative,,negative,,,,,,,,,8/17/20,, +8,7/22/20,,Lorton,VA,22079,50,negative,,negative,,,,,,,,,8/17/20,, +7,7/22/20,,Columbia,SC,29229,>85,negative,,negative,,,,,,,,,8/17/20,, +6,7/22/20,,Tampa,FL,33625,20,negative,,negative,,,,,,,,,8/17/20,, +6,7/22/20,,Tampa,FL,33625,68,positive,,positive,,,,,,,,,8/17/20,, +6,7/22/20,,Tampa,FL,33625,>85,negative,,negative,,,,,,,,,8/17/20,, +6,7/22/20,,Tampa,FL,33625,46,positive,,positive,,,,,,,,,8/17/20,, +6,7/22/20,,Tampa,FL,33625,19,negative,,negative,,,,,,,,,8/17/20,, +6,7/22/20,,Tampa,FL,33625,26,negative,,negative,,,,,,,,,8/17/20,, +6,7/22/20,,Tampa,FL,33625,56,negative,,negative,,,,,,,,,8/17/20,, +6,7/22/20,,Tampa,FL,33625,53,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,14,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,38,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,72,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,5,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,17,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,35,positive,,positive,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,85,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,47,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,62,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,74,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,4,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,60,positive,,positive,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,81,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,7,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,>85,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,>85,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,66,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,55,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,82,positive,,positive,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,29,positive,,positive,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,25,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,83,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,5,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,>85,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,>85,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,77,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,58,positive,,positive,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,6,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,36,negative,,negative,,,,,,,,,8/17/20,, +9,7/23/20,,Tampa,FL,33625,24,negative,,negative,,,,,,,,,8/17/20,, \ No newline at end of file diff --git a/quidel_covidtest/tests/test_data_tools.py b/quidel_covidtest/tests/test_data_tools.py index cb611b8f5..7125c8eef 100644 --- a/quidel_covidtest/tests/test_data_tools.py +++ b/quidel_covidtest/tests/test_data_tools.py @@ -76,8 +76,8 @@ def test__slide_window_sum(self, k, expected): @pytest.mark.parametrize("min_obs, expected", [ (1, np.array([0, 0, 0, 0])), - (2, np.array([1/2, 0, 0, 0])), - (8, np.array([1, 1, 5/6, 4/8])), + (2, np.array([1/2, 0, 0, 0])), #(2, np.array([1/2, 0, 0, 0])), + (8, np.array([1/2, 2/4, 3/6, 4/8])), #(8, np.array([1, 1, 5/6, 4/8])), ]) def test__geographical_pooling(self, min_obs, expected): tpooled_tests = np.array([1, 2, 3, 4]) @@ -135,10 +135,19 @@ def test_raw_positive_prop(self, min_obs, expected_pos_prop, expected_se, expect np.array([3, 7, 9, 11]), np.array([5, 10, 15, 20]), np.array([(1 + 0.6 + 0.5)/(2 + 1 + 1), 3.5/7, 5.5/11, 7.5/17]), - np.array([np.sqrt(2.1*(4-2.1)/4/4/3), np.sqrt(3.5*(7-3.5)/7/7/6), - np.sqrt(5.5*(11-5.5)/11/11/10), np.sqrt(7.5*(17-7.5)/17/17/16)]), + np.array([np.sqrt(2.1*(4-2.1)/4/4/3), np.sqrt(3.5*(7-3.5)/7/7/6), + np.sqrt(5.5*(11-5.5)/11/11/10), np.sqrt(7.5*(17-7.5)/17/17/16)]), np.array([3, 6, 10, 16]), ), + (5, # parents case, borrow too much + 2, + np.array([3, 7, 9, 11]), + np.array([5, 10, 15, 20]), + np.array([np.nan, 3.5/7, 5.5/11, 7.5/17]), + np.array([np.nan, np.sqrt(3.5*(7-3.5)/7/7/6), + np.sqrt(5.5*(11-5.5)/11/11/10), np.sqrt(7.5*(17-7.5)/17/17/16)]), + np.array([np.nan, 6, 10, 16]), + ), ]) def test_smoothed_positive_prop(self, min_obs, pool_days, parent_positives, parent_tests, expected_prop, expected_se, expected_sample_sz): diff --git a/quidel_covidtest/tests/test_generate_sensor.py b/quidel_covidtest/tests/test_generate_sensor.py index 0b34919c6..0ef82de83 100644 --- a/quidel_covidtest/tests/test_generate_sensor.py +++ b/quidel_covidtest/tests/test_generate_sensor.py @@ -21,7 +21,9 @@ def test_generate_sensor(self): # raw pct_positive state_pct_positive = generate_sensor_for_nonparent_geo( state_groups, "state_id", smooth = False, device = False, - first_date = datetime(2020, 6, 14), last_date = datetime(2020, 6, 20)) + first_date = datetime(2020, 6, 14), + last_date = datetime(2020, 6, 20), + suffix="total") assert (state_pct_positive.dropna()["val"] < 100).all() assert set(state_pct_positive.columns) ==\ @@ -31,7 +33,9 @@ def test_generate_sensor(self): # raw test_per_device state_test_per_device = generate_sensor_for_nonparent_geo( state_groups, "state_id", smooth = False, device = True, - first_date = datetime(2020, 6, 14), last_date = datetime(2020, 6, 20)) + first_date = datetime(2020, 6, 14), + last_date = datetime(2020, 6, 20), + suffix="total") assert state_test_per_device["se"].isnull().all() assert set(state_test_per_device.columns) ==\ @@ -45,7 +49,9 @@ def test_generate_sensor(self): parse_dates=['timestamp']) msa_pct_positive = generate_sensor_for_parent_geo( state_groups, msa_data, "cbsa_id", smooth = True, device = False, - first_date = datetime(2020, 6, 14), last_date = datetime(2020, 6, 20)) + first_date = datetime(2020, 6, 14), + last_date = datetime(2020, 6, 20), + suffix="total") assert (msa_pct_positive.dropna()["val"] < 100).all() assert set(msa_pct_positive.columns) ==\ @@ -55,7 +61,9 @@ def test_generate_sensor(self): # smoothed test_per_device msa_test_per_device = generate_sensor_for_parent_geo( state_groups, msa_data, "cbsa_id", smooth = True, device = True, - first_date = datetime(2020, 6, 14), last_date = datetime(2020, 6, 20)) + first_date = datetime(2020, 6, 14), + last_date = datetime(2020, 6, 20), + suffix="total") assert msa_test_per_device["se"].isnull().all() assert set(msa_test_per_device.columns) ==\ diff --git a/quidel_covidtest/tests/test_geo_maps.py b/quidel_covidtest/tests/test_geo_maps.py index eb1be3796..b1c74af22 100644 --- a/quidel_covidtest/tests/test_geo_maps.py +++ b/quidel_covidtest/tests/test_geo_maps.py @@ -1,29 +1,64 @@ +from datetime import datetime + import pandas as pd -from delphi_quidel_covidtest.geo_maps import geo_map +from delphi_quidel_covidtest.geo_maps import geo_map, add_megacounties +from delphi_quidel_covidtest.constants import AGE_GROUPS +DATA_COLS = ['totalTest', 'numUniqueDevices', 'positiveTest'] class TestGeoMap: def test_county(self): df = pd.DataFrame( { - "zip": [1607, 1740, 98661, 76010, 76012, 76016], - "timestamp": ["2020-06-15", "2020-06-15", "2020-06-15", - "2020-06-15", "2020-06-15", "2020-06-15"], - "totalTest": [100, 50, 200, 200, 250, 500], - "positiveTest": [10, 8, 15, 5, 20, 50], - "numUniqueDevices": [2, 1, 1, 1, 1, 1] + "zip": [1607, 1740, 1001, 1003, 98661, 76010, 76012, 76016], + "timestamp": ["2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15", + "2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15"], + "totalTest_total": [10, 16, 10, 15, 20, 200, 250, 500], + "positiveTest_total": [6, 8, 6, 8, 5, 5, 20, 50], + "numUniqueDevices_total": [2, 1, 2, 1, 1, 1, 1, 1], } ) + + for agegroup in AGE_GROUPS[1:-1]: + df[f"totalTest_{agegroup}"] = [2, 3, 2, 2, 4, 40, 20, 25] + df[f"positiveTest_{agegroup}"] = [1, 1, 1, 1, 1, 1, 3, 8] + df[f"numUniqueDevices_{agegroup}"] = [2, 1, 2, 1, 1, 1, 1, 1] + + df[f"totalTest_age_0_17"] = [4, 6, 4, 4, 8, 80, 40, 50] + df[f"positiveTest_age_0_17"] = [2, 2, 2, 2, 2, 2, 6, 16] + df[f"numUniqueDevices_age_0_17"] = [2, 1, 2, 1, 1, 1, 1, 1] new_df, res_key = geo_map("county", df) assert res_key == 'fips' - assert set(new_df["fips"].values) == set(['25027', '53011', '48439']) + assert set(new_df["fips"].values) == set(['25027', '25013', '25015', '53011', '48439']) assert set(new_df["timestamp"].values) == set(df["timestamp"].values) - assert set(new_df["totalTest"].values) == set([150, 200, 950]) - assert set(new_df["positiveTest"].values) == set([18, 15, 75]) + assert set(new_df["totalTest_total"].values) == set([26, 10, 15, 20, 950]) + assert set(new_df["positiveTest_total"].values) == set([14, 6, 8, 5, 75]) + + assert set(new_df["totalTest_age_0_4"].values) == set([5, 2, 2, 85, 4]) + assert set(new_df["positiveTest_age_0_4"].values) == set([2, 1, 1, 12, 1]) + + assert set(new_df["totalTest_age_0_17"].values) == set([10, 4, 4, 170, 8]) + assert set(new_df["positiveTest_age_0_17"].values) == set([4, 2, 2, 24, 2]) + + # Test Megacounties + new_df["timestamp"] = [datetime.strptime(x, "%Y-%m-%d") for x in new_df["timestamp"]] + mega_df = add_megacounties(new_df, True) + + assert set(mega_df["totalTest_total"].values) == set([26, 10, 15, 20, 950, 25, 20]) + assert set(mega_df["positiveTest_total"].values) == set([14, 6, 8, 5, 75, 14, 5]) + + assert set(mega_df["totalTest_age_0_4"].values) == set([5, 2, 2, 85, 4, 4, 9, 4]) + assert set(mega_df["positiveTest_age_0_4"].values) == set([2, 1, 1, 12, 1, 4, 1]) + + assert set(mega_df["totalTest_age_0_17"].values) == set([10, 4, 4, 170, 8, 8, 18, 8]) + assert set(mega_df["positiveTest_age_0_17"].values) == set([4, 2, 2, 24, 2, 8, 2]) + + assert set(mega_df["state_id"].values) == set(["ma", "tx", "wa"]) + def test_state(self): @@ -32,18 +67,33 @@ def test_state(self): "zip": [1607, 1740, 98661, 76010, 76012, 76016], "timestamp": ["2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15"], - "totalTest": [100, 50, 200, 200, 250, 500], - "positiveTest": [10, 8, 15, 5, 20, 50], - "numUniqueDevices": [2, 1, 1, 1, 1, 1] + "totalTest_total": [100, 50, 200, 200, 250, 500], + "positiveTest_total": [10, 8, 15, 5, 20, 50], + "numUniqueDevices_total": [2, 1, 1, 1, 1, 1] } - ) + ) + for agegroup in AGE_GROUPS[1:-1]: + df[f"totalTest_{agegroup}"] = [20, 10, 20, 40, 20, 25] + df[f"positiveTest_{agegroup}"] = [2, 1, 3, 1, 3, 8] + df[f"numUniqueDevices_{agegroup}"] = [2, 1, 1, 1, 1, 1] + + df[f"totalTest_age_0_17"] = [40, 20, 40, 80, 40, 50] + df[f"positiveTest_age_0_17"] = [4, 2, 6, 2, 6, 16] + df[f"numUniqueDevices_age_0_17"] = [2, 1, 1, 1, 1, 1] + new_df, res_key = geo_map("state", df) assert set(new_df["state_id"].values) == set(['ma', 'tx', 'wa']) assert set(new_df["timestamp"].values) == set(df["timestamp"].values) - assert set(new_df["totalTest"].values) == set([150, 200, 950]) - assert set(new_df["positiveTest"].values) == set([18, 15, 75]) + assert set(new_df["totalTest_total"].values) == set([150, 200, 950]) + assert set(new_df["positiveTest_total"].values) == set([18, 15, 75]) + + assert set(new_df["totalTest_age_0_4"].values) == set([30, 85, 20]) + assert set(new_df["positiveTest_age_0_4"].values) == set([3, 12, 3]) + + assert set(new_df["totalTest_age_0_17"].values) == set([60, 170, 40]) + assert set(new_df["positiveTest_age_0_17"].values) == set([6, 24, 6]) def test_hrr(self): @@ -52,18 +102,32 @@ def test_hrr(self): "zip": [1607, 98661, 76010, 76012, 74435, 74936], "timestamp": ["2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15"], - "totalTest": [100, 50, 200, 200, 250, 500], - "positiveTest": [10, 8, 15, 5, 20, 50], - "numUniqueDevices": [2, 1, 1, 1, 1, 1] + "totalTest_total": [100, 50, 200, 200, 250, 500], + "positiveTest_total": [10, 8, 15, 5, 20, 50], + "numUniqueDevices_total": [2, 1, 1, 1, 1, 1] } ) + for agegroup in AGE_GROUPS[1:-1]: + df[f"totalTest_{agegroup}"] = [20, 10, 20, 40, 20, 25] + df[f"positiveTest_{agegroup}"] = [2, 1, 3, 1, 3, 8] + df[f"numUniqueDevices_{agegroup}"] = [2, 1, 1, 1, 1, 1] + + df[f"totalTest_age_0_17"] = [40, 20, 40, 80, 40, 50] + df[f"positiveTest_age_0_17"] = [4, 2, 6, 2, 6, 16] + df[f"numUniqueDevices_age_0_17"] = [2, 1, 1, 1, 1, 1] new_df, _ = geo_map("hrr", df) assert set(new_df["hrr"].values) == set(["16", "231", "340", "344", "394"]) assert set(new_df["timestamp"].values) == set(df["timestamp"].values) - assert set(new_df["totalTest"].values) == set([500, 100, 250, 50, 400]) - assert set(new_df["positiveTest"].values) == set([50, 10, 20, 8, 20]) + assert set(new_df["totalTest_total"].values) == set([500, 100, 250, 50, 400]) + assert set(new_df["positiveTest_total"].values) == set([50, 10, 20, 8, 20]) + + assert set(new_df["totalTest_age_0_4"].values) == set([25, 20 ,20, 10, 60]) + assert set(new_df["positiveTest_age_0_4"].values) == set([8, 2, 3, 1, 4]) + + assert set(new_df["totalTest_age_0_17"].values) == set([50, 40, 40, 20, 120]) + assert set(new_df["positiveTest_age_0_17"].values) == set([16, 4, 6, 2, 8]) def test_msa(self): @@ -72,18 +136,32 @@ def test_msa(self): "zip": [1607, 73716, 73719, 76010, 74945, 74936], "timestamp": ["2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15"], - "totalTest": [100, 50, 200, 200, 250, 500], - "positiveTest": [10, 8, 15, 5, 20, 50], - "numUniqueDevices": [2, 1, 1, 1, 1, 1] + "totalTest_total": [100, 50, 200, 200, 250, 500], + "positiveTest_total": [10, 8, 15, 5, 20, 50], + "numUniqueDevices_total": [2, 1, 1, 1, 1, 1] } ) + for agegroup in AGE_GROUPS[1:-1]: + df[f"totalTest_{agegroup}"] = [20, 10, 20, 40, 20, 25] + df[f"positiveTest_{agegroup}"] = [2, 1, 3, 1, 3, 8] + df[f"numUniqueDevices_{agegroup}"] = [2, 1, 1, 1, 1, 1] + + df[f"totalTest_age_0_17"] = [40, 20, 40, 80, 40, 50] + df[f"positiveTest_age_0_17"] = [4, 2, 6, 2, 6, 16] + df[f"numUniqueDevices_age_0_17"] = [2, 1, 1, 1, 1, 1] new_df, res_key = geo_map("msa", df) assert set(new_df["msa"].values) == set(['19100', '22900', '49340']) assert set(new_df["timestamp"].values) == set(df["timestamp"].values) - assert set(new_df["totalTest"].values) == set([200, 750, 100]) - assert set(new_df["positiveTest"].values) == set([5, 70, 10]) + assert set(new_df["totalTest_total"].values) == set([200, 750, 100]) + assert set(new_df["positiveTest_total"].values) == set([5, 70, 10]) + + assert set(new_df["totalTest_age_0_4"].values) == set([40, 45, 20]) + assert set(new_df["positiveTest_age_0_4"].values) == set([1, 11, 2]) + + assert set(new_df["totalTest_age_0_17"].values) == set([80, 90, 40]) + assert set(new_df["positiveTest_age_0_17"].values) == set([2, 22, 4]) def test_nation(self): df = pd.DataFrame( @@ -91,18 +169,32 @@ def test_nation(self): "zip": [1607, 73716, 73719, 76010, 74945, 74936], "timestamp": ["2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15"], - "totalTest": [100, 50, 200, 200, 250, 500], - "positiveTest": [10, 8, 15, 5, 20, 50], - "numUniqueDevices": [2, 1, 1, 1, 1, 1] + "totalTest_total": [100, 50, 200, 200, 250, 500], + "positiveTest_total": [10, 8, 15, 5, 20, 50], + "numUniqueDevices_total": [2, 1, 1, 1, 1, 1] } ) + for agegroup in AGE_GROUPS[1:-1]: + df[f"totalTest_{agegroup}"] = [20, 10, 20, 40, 20, 25] + df[f"positiveTest_{agegroup}"] = [2, 1, 3, 1, 3, 8] + df[f"numUniqueDevices_{agegroup}"] = [2, 1, 1, 1, 1, 1] + + df[f"totalTest_age_0_17"] = [40, 20, 40, 80, 40, 50] + df[f"positiveTest_age_0_17"] = [4, 2, 6, 2, 6, 16] + df[f"numUniqueDevices_age_0_17"] = [2, 1, 1, 1, 1, 1] new_df, res_key = geo_map("nation", df) assert set(new_df["nation"].values) == set(["us"]) assert set(new_df["timestamp"].values) == set(df["timestamp"].values) - assert set(new_df["totalTest"].values) == set([1300]) - assert set(new_df["positiveTest"].values) == set([108]) + assert set(new_df["totalTest_total"].values) == set([1300]) + assert set(new_df["positiveTest_total"].values) == set([108]) + + assert set(new_df["totalTest_age_0_4"].values) == set([135]) + assert set(new_df["positiveTest_age_0_4"].values) == set([18]) + + assert set(new_df["totalTest_age_0_17"].values) == set([270]) + assert set(new_df["positiveTest_age_0_17"].values) == set([36]) def test_hhs(self): df = pd.DataFrame( @@ -110,15 +202,30 @@ def test_hhs(self): "zip": [1607, 1740, 98661, 76010, 76012, 76016], "timestamp": ["2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15", "2020-06-15"], - "totalTest": [100, 50, 200, 200, 250, 500], - "positiveTest": [10, 8, 15, 5, 20, 50], - "numUniqueDevices": [2, 1, 1, 1, 1, 1] + "totalTest_total": [100, 50, 200, 200, 250, 500], + "positiveTest_total": [10, 8, 15, 5, 20, 50], + "numUniqueDevices_total": [2, 1, 1, 1, 1, 1] } ) + for agegroup in AGE_GROUPS[1:-1]: + df[f"totalTest_{agegroup}"] = [20, 10, 20, 40, 20, 25] + df[f"positiveTest_{agegroup}"] = [2, 1, 3, 1, 3, 8] + df[f"numUniqueDevices_{agegroup}"] = [2, 1, 1, 1, 1, 1] + + df[f"totalTest_age_0_17"] = [40, 20, 40, 80, 40, 50] + df[f"positiveTest_age_0_17"] = [4, 2, 6, 2, 6, 16] + df[f"numUniqueDevices_age_0_17"] = [2, 1, 1, 1, 1, 1] new_df, res_key = geo_map("hhs", df) assert set(new_df["hhs"].values) == set(["1", "6", "10"]) assert set(new_df["timestamp"].values) == set(df["timestamp"].values) - assert set(new_df["totalTest"].values) == set([150, 200, 950]) - assert set(new_df["positiveTest"].values) == set([18, 15, 75]) + assert set(new_df["totalTest_total"].values) == set([150, 200, 950]) + assert set(new_df["positiveTest_total"].values) == set([18, 15, 75]) + + assert set(new_df["totalTest_age_0_4"].values) == set([30, 20, 85]) + assert set(new_df["positiveTest_age_0_4"].values) == set([3, 3, 12]) + + assert set(new_df["totalTest_age_0_17"].values) == set([60, 40, 170]) + assert set(new_df["positiveTest_age_0_17"].values) == set([6, 6, 24]) + diff --git a/quidel_covidtest/tests/test_pull.py b/quidel_covidtest/tests/test_pull.py index 17ddbb6fd..ae35fc68f 100644 --- a/quidel_covidtest/tests/test_pull.py +++ b/quidel_covidtest/tests/test_pull.py @@ -11,6 +11,7 @@ check_export_end_date, check_export_start_date ) +from delphi_quidel_covidtest.constants import AGE_GROUPS END_FROM_TODAY_MINUS = 5 EXPORT_DAY_RANGE = 40 @@ -60,8 +61,10 @@ def test_pull_quidel_covidtest(self): assert [first_date.month, first_date.day] == [7, 18] assert [last_date.month, last_date.day] == [7, 23] - assert (df.columns ==\ - ['timestamp', 'zip', 'totalTest', 'numUniqueDevices', 'positiveTest']).all() + assert set(['timestamp', 'zip']).issubset(set(df.columns)) + for agegroup in AGE_GROUPS: + set([f'totalTest_{agegroup}', f'numUniqueDevices_{agegroup}', + f'positiveTest_{agegroup}']).issubset(set(df.columns)) def test_check_intermediate_file(self): diff --git a/quidel_covidtest/tests/test_run.py b/quidel_covidtest/tests/test_run.py index 89c59a24f..d9e28108a 100644 --- a/quidel_covidtest/tests/test_run.py +++ b/quidel_covidtest/tests/test_run.py @@ -3,8 +3,9 @@ from os.path import join import pandas as pd +import numpy as np -from delphi_utils import read_params, add_prefix +from delphi_utils import add_prefix from delphi_quidel_covidtest.constants import PARENT_GEO_RESOLUTIONS, NONPARENT_GEO_RESOLUTIONS, \ SENSORS from delphi_quidel_covidtest.run import run_module @@ -68,6 +69,34 @@ def test_output_files(self, clean_receiving_dir): ) assert (df.columns.values == ["geo_id", "val", "se", "sample_size"]).all() + df = pd.read_csv(join("./receiving", "20200718_county_covid_ag_raw_pct_positive.csv")) + #ZIP 24534, FIPS 51083 has 4 counts, 2 positives + #ZIP 24529, FIPS 51117 has 24 counts, 12 positives + #ZIP 24526, FIPS 51019 has 49 counts, 26 positives + #MEGAFIPS 51000, should have 4+24+49 = 77 counts, 2+12+26 = 40 positives + #ZIP 24527, FIPS 51143 has 64 counts, 32 positives + #ZIP 22079, FIPS 51059 has 60 counts, 24 positives + assert set(df.geo_id) == set([51143, 51059, 51000]) + assert set(df.sample_size) == set([64, 60, 77]) + assert np.allclose(df.val.values, [(40+0.5)/(77+1)*100, (24+0.5)/(60+1)*100, + (32+0.5)/(64+1)*100], equal_nan=True) + + + df = pd.read_csv(join("./receiving", "20200718_county_covid_ag_smoothed_pct_positive.csv")) + assert set(df.geo_id) == set([51000, 51019, 51143, 51059]) + assert set(df.sample_size) == set([50, 50, 64, 60]) + parent_test = 4+24+49+64+60 + parent_pos = 2+12+26+32+24 + #ZIP 24526, FIPS 51019 has 49 counts, 26 positives, borrow 1 pseudo counts from VA + #MEGAFIPS 51000, should have 4+24 = 28 counts, 2+12 = 14 positives, borrow 22 pseudo counts + #ZIP 24527, FIPS 51143 has 64 counts, 32 positives, do not borrow + #ZIP 22079, FIPS 51059 has 60 counts, 24 positives, do not borrow + assert np.allclose(df.val.values, + [(14+parent_pos*22/parent_test+0.5)/(50+1)*100, + (26+parent_pos*1/parent_test+0.5)/(50+1)*100, + (24+0.5)/(60+1)*100, + (32+0.5)/(64+1)*100], equal_nan=True) + # test_intermediate_file flag = None for fname in listdir("./cache"):