apmtools 0.2.31__tar.gz → 0.2.33__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {apmtools-0.2.31 → apmtools-0.2.33}/PKG-INFO +1 -1
- {apmtools-0.2.31 → apmtools-0.2.33}/pyproject.toml +1 -1
- {apmtools-0.2.31 → apmtools-0.2.33}/src/apmtools/classes.py +131 -8
- {apmtools-0.2.31 → apmtools-0.2.33}/src/apmtools/data_processing.py +14 -1
- {apmtools-0.2.31 → apmtools-0.2.33}/src/apmtools/shiny/summary.py +912 -713
- {apmtools-0.2.31 → apmtools-0.2.33}/src/apmtools/units.py +4 -0
- {apmtools-0.2.31 → apmtools-0.2.33}/.gitignore +0 -0
- {apmtools-0.2.31 → apmtools-0.2.33}/LICENSE +0 -0
- {apmtools-0.2.31 → apmtools-0.2.33}/README.md +0 -0
- {apmtools-0.2.31 → apmtools-0.2.33}/src/apmtools/__init__.py +0 -0
- {apmtools-0.2.31 → apmtools-0.2.33}/src/apmtools/functions.py +0 -0
- {apmtools-0.2.31 → apmtools-0.2.33}/src/apmtools/legacy.py +0 -0
- {apmtools-0.2.31 → apmtools-0.2.33}/src/apmtools/shiny/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: apmtools
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.33
|
|
4
4
|
Summary: A collection of tools for processing air pollution monitoring data
|
|
5
5
|
Project-URL: Homepage, https://github.com/federlorenz/apmtools
|
|
6
6
|
Author-email: Federico Lorenzetti <lorenzetti.federico@gmail.com>
|
|
@@ -8,7 +8,7 @@ from io import StringIO
|
|
|
8
8
|
from csv import writer
|
|
9
9
|
from dateutil.parser import parse
|
|
10
10
|
|
|
11
|
-
from .units import upas_units,lascar_units,purple_units
|
|
11
|
+
from .units import upas_units, lascar_units, purple_units, lcm_units
|
|
12
12
|
|
|
13
13
|
class DictionaryPlus(dict):
|
|
14
14
|
def __init__(self, *args, **kwargs):
|
|
@@ -451,11 +451,11 @@ class Dataset(DictionaryPlus):
|
|
|
451
451
|
|
|
452
452
|
def scan_folder(self, directory="./", levels=[], level=0, monitor=None, levels_dict=None, gmt_timezone_shift=0, interpolate=False, add_m={}):
|
|
453
453
|
|
|
454
|
-
from .data_processing import upas_processing, lascar_processing, purple_processing
|
|
454
|
+
from .data_processing import upas_processing, lascar_processing, purple_processing, lcm_processing
|
|
455
455
|
|
|
456
|
-
if len(set(levels).intersection(set(["identifier", "start", "end", "length", "time", "pm25", "pm25correctedrh", "pm25correctedrhandgrav", "filterid", "grav_not", "grav"]))) > 0:
|
|
456
|
+
if len(set(levels).intersection(set(["identifier", "start", "end", "length", "time", "pm25", "pm25correctedrh", "pm25correctedrhandgrav", "filterid", "grav_not", "grav","PM2.5","PM10","C02","NO2"]))) > 0:
|
|
457
457
|
print(
|
|
458
|
-
f"the following levels names are not allowed: {set(levels).intersection(set(["identifier", "start", "end", "length", "time", "pm25", "pm25correctedrh", "pm25correctedrhandgrav", "filterid", "grav_not", "grav"]))}. Please choose different level names")
|
|
458
|
+
f"the following levels names are not allowed: {set(levels).intersection(set(["identifier", "start", "end", "length", "time", "pm25", "pm25correctedrh", "pm25correctedrhandgrav", "filterid", "grav_not", "grav","PM2.5","PM10","C02","NO2"]))}. Please choose different level names")
|
|
459
459
|
return
|
|
460
460
|
if levels_dict == None:
|
|
461
461
|
levels_dict = dict(
|
|
@@ -473,6 +473,8 @@ class Dataset(DictionaryPlus):
|
|
|
473
473
|
return "purple"
|
|
474
474
|
case ['Time', 'CO(ppm)']:
|
|
475
475
|
return "lascar"
|
|
476
|
+
case ['sensorName', 'deviceId']:
|
|
477
|
+
return "lcm"
|
|
476
478
|
case _:
|
|
477
479
|
return None
|
|
478
480
|
except:
|
|
@@ -518,6 +520,21 @@ class Dataset(DictionaryPlus):
|
|
|
518
520
|
except:
|
|
519
521
|
print(
|
|
520
522
|
f"processing of file {directory}{j} failed. Is this a {monitor} file?")
|
|
523
|
+
case "lcm":
|
|
524
|
+
try:
|
|
525
|
+
processed = lcm_processing(
|
|
526
|
+
directory, file=j, interpolate_data=interpolate)
|
|
527
|
+
for k, v in levels_dict.items():
|
|
528
|
+
if (v not in ["None", "_", "-", None]) and (k not in ["monitor", "", None, "-", "_"]):
|
|
529
|
+
processed.m[k] = v
|
|
530
|
+
processed.m["rejected"] = False
|
|
531
|
+
processed.m["filename"] = j
|
|
532
|
+
for key, value in add_m.items():
|
|
533
|
+
processed.m[key] = value
|
|
534
|
+
self[str(uuid.uuid4())] = processed
|
|
535
|
+
except:
|
|
536
|
+
print(
|
|
537
|
+
f"processing of file {directory}{j} failed. Is this a {monitor} file?")
|
|
521
538
|
case "purple":
|
|
522
539
|
try:
|
|
523
540
|
processed = purple_processing(
|
|
@@ -559,6 +576,8 @@ class Dataset(DictionaryPlus):
|
|
|
559
576
|
return ("lascar", run_lascar())
|
|
560
577
|
elif x is Purple:
|
|
561
578
|
return ("purple", run_purple())
|
|
579
|
+
elif x is Lcm:
|
|
580
|
+
return ("lcm", run_lcm())
|
|
562
581
|
# elif x is Apm:
|
|
563
582
|
# return "Apm"
|
|
564
583
|
# elif x is PolarH10:
|
|
@@ -572,6 +591,8 @@ class Dataset(DictionaryPlus):
|
|
|
572
591
|
return "Upas"
|
|
573
592
|
case Lascar():
|
|
574
593
|
return "Lascar"
|
|
594
|
+
case Lcm():
|
|
595
|
+
return "Lcm"
|
|
575
596
|
case Purple():
|
|
576
597
|
return "Purple"
|
|
577
598
|
case Apm():
|
|
@@ -702,6 +723,58 @@ class Dataset(DictionaryPlus):
|
|
|
702
723
|
out["time"] = pd.to_datetime(out["time"])
|
|
703
724
|
return (Summary(out, metadata=set(columns2)))
|
|
704
725
|
|
|
726
|
+
def run_lcm():
|
|
727
|
+
a = 0.524
|
|
728
|
+
b = -0.0862
|
|
729
|
+
c = 5.75
|
|
730
|
+
d = self.subset(condition=lambda x: match_monitor(x) == "Lcm")
|
|
731
|
+
columns1 = ["identifier", "monitor",
|
|
732
|
+
"start", "end", "length", "time"]
|
|
733
|
+
columns2 = list(set(d.meta()))
|
|
734
|
+
columns3 = ["sensorName", "deviceId", "resolution",
|
|
735
|
+
"PM2.5", "PM10", "CO2", "NO2", "Temperature", "Humidity"]
|
|
736
|
+
columns4 = ["pm25correctedrh"]
|
|
737
|
+
|
|
738
|
+
buffer = StringIO()
|
|
739
|
+
with buffer as file:
|
|
740
|
+
df = writer(file, delimiter=",")
|
|
741
|
+
df.writerow(columns1+columns2+columns3+columns4)
|
|
742
|
+
count = 0
|
|
743
|
+
for k, v in d.items():
|
|
744
|
+
print(f"processing Lcm {count+1} out of {len(d)}")
|
|
745
|
+
v = v.dropna(subset="PM2.5")
|
|
746
|
+
identifier = k
|
|
747
|
+
start = v.start
|
|
748
|
+
end = v.end
|
|
749
|
+
length = v.length
|
|
750
|
+
for i in range(len(v)):
|
|
751
|
+
app = []
|
|
752
|
+
app.append(identifier)
|
|
753
|
+
app.append("lcm")
|
|
754
|
+
app.append(start)
|
|
755
|
+
app.append(end)
|
|
756
|
+
app.append(length)
|
|
757
|
+
app.append(v.index[i])
|
|
758
|
+
for m in columns2:
|
|
759
|
+
if m in v.m.keys():
|
|
760
|
+
app.append(v.m[m] if v.m[m] != None else "")
|
|
761
|
+
else:
|
|
762
|
+
app.append("")
|
|
763
|
+
for m in columns3:
|
|
764
|
+
app.append(v[m].iloc[i])
|
|
765
|
+
xx = (a*v["PM2.5"].iloc[i])+(b*v["Humidity"].iloc[i])+c
|
|
766
|
+
app.append(xx)
|
|
767
|
+
df.writerow(list(map(str, app)))
|
|
768
|
+
count += 1
|
|
769
|
+
file.seek(0)
|
|
770
|
+
out = pd.read_csv(file)
|
|
771
|
+
out["start"] = pd.to_datetime(out["start"])
|
|
772
|
+
out["end"] = pd.to_datetime(out["end"])
|
|
773
|
+
out["length"] = pd.to_timedelta(out["length"])
|
|
774
|
+
out["time"] = pd.to_datetime(out["time"])
|
|
775
|
+
return (Summary(out, metadata=set(columns2)))
|
|
776
|
+
|
|
777
|
+
|
|
705
778
|
def run_purple():
|
|
706
779
|
d = self.subset(condition=lambda x: match_monitor(x) == "Purple")
|
|
707
780
|
columns1 = ["identifier", "monitor",
|
|
@@ -819,6 +892,8 @@ class Dataset(DictionaryPlus):
|
|
|
819
892
|
return "Upas"
|
|
820
893
|
case Lascar():
|
|
821
894
|
return "Lascar"
|
|
895
|
+
case Lcm():
|
|
896
|
+
return "Lcm"
|
|
822
897
|
case Purple():
|
|
823
898
|
return "Purple"
|
|
824
899
|
case Apm():
|
|
@@ -1454,9 +1529,26 @@ class Summary(pd.DataFrame):
|
|
|
1454
1529
|
else:
|
|
1455
1530
|
output = df
|
|
1456
1531
|
|
|
1457
|
-
|
|
1532
|
+
if isinstance(frequency, str) and aggregate is None:
|
|
1533
|
+
if grouping_by != None:
|
|
1534
|
+
output = df[grouping_by+[frequency]]
|
|
1535
|
+
else:
|
|
1536
|
+
output = df[[frequency]]
|
|
1537
|
+
if isinstance(frequency, list) and aggregate is None:
|
|
1538
|
+
if grouping_by != None:
|
|
1539
|
+
output = df[grouping_by+frequency]
|
|
1540
|
+
else:
|
|
1541
|
+
output = df[frequency]
|
|
1542
|
+
if frequency == None and aggregate is None:
|
|
1543
|
+
if grouping_by != None:
|
|
1544
|
+
output = df[grouping_by]
|
|
1545
|
+
else:
|
|
1546
|
+
output = df
|
|
1547
|
+
|
|
1548
|
+
if aggregate is not None:
|
|
1549
|
+
output = output[value_cols].agg(aggregate)
|
|
1458
1550
|
|
|
1459
|
-
if frequency != None or grouping_by != None:
|
|
1551
|
+
if aggregate != None and (frequency != None or grouping_by != None):
|
|
1460
1552
|
output.columns = [
|
|
1461
1553
|
"_".join(str(x) for x in col if pd.notna(x) and x != "")
|
|
1462
1554
|
for col in output.columns.to_flat_index()
|
|
@@ -1472,8 +1564,8 @@ class Summary(pd.DataFrame):
|
|
|
1472
1564
|
output.m["aggregate"] = aggregate
|
|
1473
1565
|
|
|
1474
1566
|
return output
|
|
1475
|
-
|
|
1476
|
-
|
|
1567
|
+
|
|
1568
|
+
|
|
1477
1569
|
def get_monitor(self, value):
|
|
1478
1570
|
|
|
1479
1571
|
out = self.subset({"monitor": [value]})
|
|
@@ -1936,6 +2028,37 @@ class PurpleSeries(ApmSeries):
|
|
|
1936
2028
|
def _constructor(self):
|
|
1937
2029
|
return PurpleSeries
|
|
1938
2030
|
|
|
2031
|
+
class Lcm(Apm):
|
|
2032
|
+
monitor = "lcm"
|
|
2033
|
+
|
|
2034
|
+
def __init__(self, *args, **kwargs):
|
|
2035
|
+
Apm.__init__(self, *args, **kwargs)
|
|
2036
|
+
self.m = {}
|
|
2037
|
+
self.variable = "PM2.5"
|
|
2038
|
+
|
|
2039
|
+
@property
|
|
2040
|
+
def _constructor(self):
|
|
2041
|
+
return Lcm
|
|
2042
|
+
|
|
2043
|
+
@property
|
|
2044
|
+
def _constructor_sliced(self):
|
|
2045
|
+
return LcmSeries
|
|
2046
|
+
|
|
2047
|
+
@property
|
|
2048
|
+
def units(self):
|
|
2049
|
+
return lcm_units
|
|
2050
|
+
|
|
2051
|
+
class LcmSeries(ApmSeries):
|
|
2052
|
+
def __init__(self, *args, **kwargs):
|
|
2053
|
+
LcmSeries.__init__(self, *args, **kwargs)
|
|
2054
|
+
_metadata = ['m']
|
|
2055
|
+
|
|
2056
|
+
@property
|
|
2057
|
+
def _constructor(self):
|
|
2058
|
+
return LcmSeries
|
|
2059
|
+
|
|
2060
|
+
|
|
2061
|
+
|
|
1939
2062
|
class PolarH10(dict):
|
|
1940
2063
|
monitor = "polarh10"
|
|
1941
2064
|
def __init__(self, *args, **kwargs):
|
|
@@ -4,7 +4,7 @@ import pandas as pd
|
|
|
4
4
|
import numpy as np
|
|
5
5
|
import datetime as dt
|
|
6
6
|
import os as os
|
|
7
|
-
from .classes import Apm, Sum, PolarH10, DictionaryPlus, Grav_Filter, Upas, Lascar, Purple
|
|
7
|
+
from .classes import Apm, Sum, PolarH10, DictionaryPlus, Grav_Filter, Upas, Lascar, Purple, Lcm
|
|
8
8
|
from zipfile import ZipFile
|
|
9
9
|
from copy import deepcopy
|
|
10
10
|
from pandas.errors import EmptyDataError
|
|
@@ -841,6 +841,19 @@ def lascar_processing(directory, file, interpolation=1,interval="30 seconds", in
|
|
|
841
841
|
out = Lascar(df)
|
|
842
842
|
return out
|
|
843
843
|
|
|
844
|
+
|
|
845
|
+
def lcm_processing(directory, file, interpolation=1, interval="300 seconds", interpolate_data=True):
|
|
846
|
+
numeric = ["PM2.5", "PM10", "CO2", "NO2", "Temperature", "Humidity"]
|
|
847
|
+
dtformat = '%Y-%m-%dT%H:%M:%S'
|
|
848
|
+
df = pd.read_csv(directory+file, index_col="timestamp",
|
|
849
|
+
date_format={'Time': dtformat})
|
|
850
|
+
if interpolate_data:
|
|
851
|
+
df = interpolate(df, 300, interpolation, pd.Timedelta(
|
|
852
|
+
'00:06:00'), numeric_columns=numeric, add_binary_counter=False)
|
|
853
|
+
df = keep_interval(df, interval)
|
|
854
|
+
out = Lcm(df)
|
|
855
|
+
return out
|
|
856
|
+
|
|
844
857
|
def sum_interpolation(file, interpolation=1, interval="5 minutes", timing=False):
|
|
845
858
|
if timing:
|
|
846
859
|
start = time.process_time()
|