PK!9x)trajpandas/__init__.py__version__ = "0.1.1" import os import glob from collections import OrderedDict as odict import warnings import numpy as np import pandas as pd from scipy.interpolate import interpn from trajpandas.io.trm import read_bin as read_trm from pandas import * @pd.api.extensions.register_dataframe_accessor("traj") class TrajAccessor(object): def __init__(self, pandas_obj): self._obj = pandas_obj if len(pandas_obj) == 0: raise pd.errors.EmptyDataError("No Trajectory data") for key in ["id", "xpos", "ypos"]: if not key in self._obj: raise KeyError(f"The row '{key}' is missing.") def setup_grid(self,latmat, lonmat): """Provide infomation about the GCM grid used to advect particles""" self.jmt,self.imt = latmat.shape self.latmat = latmat self.lonmat = lonmat def add_latlon(self, latmat=None, lonmat=None): if latmat is None: latmat = self.latmat if lonmat is None: lonmat = self.lonmat ijtup = (np.arange(lonmat.shape[0]),np.arange(lonmat.shape[1])) xyarr = self._obj[["ypos","xpos"]].values xyarr[xyarr<0] = 0 self._obj["lon"] = interpn(ijtup, lonmat, xyarr).astype(np.float32) self._obj["lat"] = interpn(ijtup, latmat, xyarr).astype(np.float32) def add_age(self): """Calculate the age since release for all postions in trdf""" age = lambda jd: jd - jd.iloc[0] self._obj["_index_time"] = self._obj.index self._obj["age"] = self._obj.groupby("id")["_index_time"].transform(age) del self._obj["_index_time"] def add_delta(self, rowname, Dxy=False): """Calculate DChl from traj dataframe""" #if rowname not in self._obj.keys(): # raise KeyError(f"The row '{rowname}' is not in the Dataframe") rowlist = ["time", rowname] if type(rowname)==str else ["time",]+rowname self._obj["time"] = self._obj.index if Dxy: rowlist += ["xpos", "ypos"] self._objd = self._obj[["id",] + rowlist].groupby("id").transform( lambda x: x.diff()) for fn in rowlist: self._obj[f"D{fn}"] = self._objd[fn] #@need_grid_info def add_dist(self, cummulative=False): """Calculate distances along all positions along all trajs.""" if not hasattr(self._obj, 'lon'): self.add_latlon() ll2 = self._obj dll = ll2.groupby("id")[["lat","lon"]].transform(lambda x: x.diff()) ll1 = ll2.groupby("id")[["lat","lon"]].transform( lambda x: np.append(np.zeros(1), x[:-1]) ) radius = 6371 * 1000 # m a = (np.sin(np.deg2rad(dll["lat"])/2)**2 + np.cos(np.deg2rad(ll1["lat"])) * np.cos(np.deg2rad(ll2["lat"])) * np.sin(np.deg2rad(dll["lon"])/2)**2) c = 2 * np.arctan2(np.sqrt(a), np.sqrt(1 - a)) self._obj["dist"] = radius * c def add_speed(self, t2=False): """Calculate the speed in m/s of the particle. Values are added to t=1 by default set t2 to True for t=2 Examples: t2=False: time xpos ypos speed 0 1.1 1.1 4.0 1 """ if not hasattr(self._obj, "dist"): self.add_dist() if not hasattr(self._obj, "Dtime"): self._obj["time"] = self._obj.index Dtime = self._obj[["id","time"]].groupby("id").transform( lambda x: x.diff()).squeeze() else: Dtime = self._obj["Dtime"] self._obj["speed"] = (self._obj["dist"].values / (Dtime.values.astype(int)/1e9)) if not t2: self._obj["speed"] = (self._obj[["id","speed"]]. groupby("id"). transform(lambda x: np.roll(x,-1))) def need_grid_info(aFunc): """Check if grid inof is loaded." def bFunc( *args, **kw ): if not "x" in dir(args[0]): raise NameError, "Trajectory data not loaded." if len(args[0].x) == 0: raise ValueError, "Trajectory data empty." return aFunc( *args, **kw ) bFunc.__name__ = aFunc.__name__ bFunc.__doc__ = aFunc.__doc__ return bFunc """ def piecewise_distance(latvec, lonvec): """Calculate the Haversine distance. Parameters ---------- origin : tuple of float (lat, long) destination : tuple of float (lat, long) Returns ------- distance_in_km : float Examples -------- >>> munich = (48.1372, 11.5756) >>> berlin = (52.5186, 13.4083) >>> round(haversine_distance(munich, berlin), 1) 504.2 >>> new_york_city = (40.712777777778, -74.005833333333) # NYC >>> round(haversine_distance(berlin, new_york_city), 1) 6385.3 """ radius = 6371 # km lat = np.deg2rad(latvec) lon = np.deg2rad(lonvec) dlat = lat[1:] - lat[:-1] dlon = lon[1:] - lon[:-1] dist = latvec * 0 a = np.sin(dlat/2)**2 + np.cos(lat[:-1])*np.cos(lat[1:])*np.sin(dlon/2)**2 c = 2 * np.arctan2(np.sqrt(a), np.sqrt(1 - a)) dist[1:] = radius * c return dist def filter_by_len(df, traj_minlen=-np.inf, traj_maxlen=np.inf): """Remove trajectories longer and shorter limits""" gr = df.groupby("id").filter(lambda x: len(x) >= traj_minlen) gr = gr.groupby("id").filter(lambda x: len(x) <= traj_maxlen) return gr def interpolate(df, dt="1h", method="cubic", traj_minlen=7): """Interpolate each trajectory to dt distances""" gr = filter_by_len(df, traj_minlen=traj_minlen) dd = gr.groupby("id").apply( lambda grp: grp.resample(dt).interpolate(method=method)) if hasattr(dd, "id"): del dd["id"] return dd.reset_index(level=0) PK!-H trajpandas/__init__.py~__version__ = "0.1.1" import os import glob from collections import OrderedDict as odict import warnings import numpy as np import pandas as pd from scipy.interpolate import interpn from pytraj.io.trm import read_bin as read_trm from pandas import * @pd.api.extensions.register_dataframe_accessor("traj") class TrajAccessor(object): def __init__(self, pandas_obj): self._obj = pandas_obj if len(pandas_obj) == 0: raise pd.errors.EmptyDataError("No Trajectory data") for key in ["id", "xpos", "ypos"]: if not key in self._obj: raise KeyError(f"The row '{key}' is missing.") def setup_grid(self,latmat, lonmat): """Provide infomation about the GCM grid used to advect particles""" self.jmt,self.imt = latmat.shape self.latmat = latmat self.lonmat = lonmat def add_latlon(self, latmat=None, lonmat=None): if latmat is None: latmat = self.latmat if lonmat is None: lonmat = self.lonmat ijtup = (np.arange(lonmat.shape[0]),np.arange(lonmat.shape[1])) xyarr = self._obj[["ypos","xpos"]].values xyarr[xyarr<0] = 0 self._obj["lon"] = interpn(ijtup, lonmat, xyarr).astype(np.float32) self._obj["lat"] = interpn(ijtup, latmat, xyarr).astype(np.float32) def add_age(self): """Calculate the age since release for all postions in trdf""" age = lambda jd: jd - jd.iloc[0] self._obj["_index_time"] = self._obj.index self._obj["age"] = self._obj.groupby("id")["_index_time"].transform(age) del self._obj["_index_time"] def add_delta(self, rowname, Dxy=False): """Calculate DChl from traj dataframe""" #if rowname not in self._obj.keys(): # raise KeyError(f"The row '{rowname}' is not in the Dataframe") rowlist = ["time", rowname] if type(rowname)==str else ["time",]+rowname self._obj["time"] = self._obj.index if Dxy: rowlist += ["xpos", "ypos"] self._objd = self._obj[["id",] + rowlist].groupby("id").transform( lambda x: x.diff()) for fn in rowlist: self._obj[f"D{fn}"] = self._objd[fn] #@need_grid_info def add_dist(self, cummulative=False): """Calculate distances along all positions along all trajs.""" if not hasattr(self._obj, 'lon'): self.add_latlon() ll2 = self._obj dll = ll2.groupby("id")[["lat","lon"]].transform(lambda x: x.diff()) ll1 = ll2.groupby("id")[["lat","lon"]].transform( lambda x: np.append(np.zeros(1), x[:-1]) ) radius = 6371 * 1000 # m a = (np.sin(np.deg2rad(dll["lat"])/2)**2 + np.cos(np.deg2rad(ll1["lat"])) * np.cos(np.deg2rad(ll2["lat"])) * np.sin(np.deg2rad(dll["lon"])/2)**2) c = 2 * np.arctan2(np.sqrt(a), np.sqrt(1 - a)) self._obj["dist"] = radius * c def add_speed(self, t2=False): """Calculate the speed in m/s of the particle. Values are added to t=1 by default set t2 to True for t=2 Examples: t2=False: time xpos ypos speed 0 1.1 1.1 4.0 1 """ if not hasattr(self._obj, "dist"): self.add_dist() if not hasattr(self._obj, "Dtime"): self._obj["time"] = self._obj.index Dtime = self._obj[["id","time"]].groupby("id").transform( lambda x: x.diff()).squeeze() else: Dtime = self._obj["Dtime"] self._obj["speed"] = (self._obj["dist"].values / (Dtime.values.astype(int)/1e9)) if not t2: self._obj["speed"] = (self._obj[["id","speed"]]. groupby("id"). transform(lambda x: np.roll(x,-1))) def need_grid_info(aFunc): """Check if grid inof is loaded." def bFunc( *args, **kw ): if not "x" in dir(args[0]): raise NameError, "Trajectory data not loaded." if len(args[0].x) == 0: raise ValueError, "Trajectory data empty." return aFunc( *args, **kw ) bFunc.__name__ = aFunc.__name__ bFunc.__doc__ = aFunc.__doc__ return bFunc """ def piecewise_distance(latvec, lonvec): """Calculate the Haversine distance. Parameters ---------- origin : tuple of float (lat, long) destination : tuple of float (lat, long) Returns ------- distance_in_km : float Examples -------- >>> munich = (48.1372, 11.5756) >>> berlin = (52.5186, 13.4083) >>> round(haversine_distance(munich, berlin), 1) 504.2 >>> new_york_city = (40.712777777778, -74.005833333333) # NYC >>> round(haversine_distance(berlin, new_york_city), 1) 6385.3 """ radius = 6371 # km lat = np.deg2rad(latvec) lon = np.deg2rad(lonvec) dlat = lat[1:] - lat[:-1] dlon = lon[1:] - lon[:-1] dist = latvec * 0 a = np.sin(dlat/2)**2 + np.cos(lat[:-1])*np.cos(lat[1:])*np.sin(dlon/2)**2 c = 2 * np.arctan2(np.sqrt(a), np.sqrt(1 - a)) dist[1:] = radius * c return dist def filter_by_len(df, traj_minlen=-np.inf, traj_maxlen=np.inf): """Remove trajectories longer and shorter limits""" gr = df.groupby("id").filter(lambda x: len(x) >= traj_minlen) gr = gr.groupby("id").filter(lambda x: len(x) <= traj_maxlen) return gr def interpolate(df, dt="1h", method="cubic", traj_minlen=7): """Interpolate each trajectory to dt distances""" gr = filter_by_len(df, traj_minlen=traj_minlen) dd = gr.groupby("id").apply( lambda grp: grp.resample(dt).interpolate(method=method)) if hasattr(dd, "id"): del dd["id"] return dd.reset_index(level=0) PK!trajpandas/io/__init__.pyPK!GGtrajpandas/io/trm.py import os import glob from collections import OrderedDict as odict import numpy as np import pandas as pd def read_bin(filename, count=-1, keep_2D_zpos=False): """Read TRACMASS binary file""" dtype = np.dtype([('id', 'i4'), ('jd', 'f8'), ('xpos','f4'), ('ypos','f4'), ('zpos','f4')]) runtraj = np.fromfile(open(filename), dtype, count=count) dt64 = ((runtraj["jd"])*24*60*60-62135683200).astype("datetime64[s]") df = pd.DataFrame(data={"id":runtraj["id"], "xpos":runtraj["xpos"]-1, "ypos":runtraj["ypos"]-1, "zpos":runtraj["zpos"]-1}, index=pd.Series(dt64)) if (not keep_2D_zpos) and (len(df["zpos"].unique())==1): del df["zpos"] df.sort_index(inplace=True) return df PK!U~trajpandas/utils/lldist.pyfrom numpy import * import numpy as np pi180 = pi/180 earth_radius = 6371.0*1000 def lldist(lon,lat): lat = lat * pi180 dlon = (lon[1:] - lon[:-1])*pi180 dlat = (lat[1:] - lat[:-1]) a = (sin(dlat/2))**2 + cos(lat[:-1]) * cos(lat[1:]) * (sin(dlon/2))**2 angles = lon * 0 angles[1:] = 2 * arctan2( sqrt(a), sqrt(1-a) ) return earth_radius * angles def ll2dist(lon,lat): lat = lat * pi180 dlon = (lon[1,:] - lon[0,:])*pi180 dlat = (lat[1,:] - lat[0,:]) a = (sin(dlat/2))**2 + cos(lat[0,:]) * cos(lat[1,:]) * (sin(dlon/2))**2 angles = 2 * arctan2( sqrt(a), sqrt(1-a) ) return earth_radius * angles def ll2dist2vec(lonvec1, latvec1, lonvec2, latvec2): """Find distance between each respective elements in two sets of vectors""" latvec1 = np.deg2rad(latvec1) latvec2 = np.deg2rad(latvec2) lonvec1 = np.deg2rad(lonvec1) lonvec2 = np.deg2rad(lonvec2) dlon = (lonvec2 - lonvec1) dlat = (latvec2 - latvec1) a = (sin(dlat/2))**2 + cos(latvec1) * cos(latvec2) * (sin(dlon/2))**2 angles = 2 * arctan2( sqrt(a), sqrt(1-a) ) return earth_radius * angles def spherical_dist(pos1, pos2, r=3958.75): pos1 = np.deg2rad(pos1) pos2 = np.deg2rad(pos2) cos_lat1 = np.cos(pos1[..., 0]) cos_lat2 = np.cos(pos2[..., 0]) cos_lat_d = np.cos(pos1[..., 0] - pos2[..., 0]) cos_lon_d = np.cos(pos1[..., 1] - pos2[..., 1]) return earth_radius * np.arccos(cos_lat_d - cos_lat1 * cos_lat2 * (1 - cos_lon_d)) def distance_matrix(lonvec, latvec): """Calculate a distance matrix for all combinations of the lat-lon positions""" latmat1,latmat2 = np.meshgrid(latvec, latvec) lonmat1,lonmat2 = np.meshgrid(lonvec, lonvec) pos1 = np.vstack((latmat1.flatten(), lonmat1.flatten())).T pos2 = np.vstack((latmat2.flatten(), lonmat2.flatten())).T dist = spherical_dist(pos1,pos2) return dist.reshape(int(np.sqrt(len(dist))),-1) PK!j*=I--"trajpandas-0.1.1.dist-info/LICENSEMIT License Copyright (c) 2019 Bror Jonsson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK!HڽTU trajpandas-0.1.1.dist-info/WHEEL A н#Z;/"d&F[xzw@Zpy3Fv]\fi4WZ^EgM_-]#0(q7PK!HWy#trajpandas-0.1.1.dist-info/METADATAKO0{Rj+^%T $1vlw;0TSFs>S.PK!H̓{ȭ!trajpandas-0.1.1.dist-info/RECORD}n@}* ,fe3fd,γvab/FEUizoTGH#:x*(dD,bw\ cʫ ,ꨦI?5͵'-/Ee6{b'ĺO+mAp!/5d~DIZXie3Y>a_\ߒ')/}Gks *׸y56)i.?[CnV)'qy,&U׬}ob}zukq *+ˋ'f~@Wzft|(\g$[Bu[/5I 7Z)P;s(Yȯzc(%dBemf $cп˷ w;SNNyNgv$4H0k 00$lДP<ϼPsPK!9x)trajpandas/__init__.pyPK!-H 4trajpandas/__init__.py~PK!e.trajpandas/io/__init__.pyPK!GG.trajpandas/io/trm.pyPK!U~2trajpandas/utils/lldist.pyPK!j*=I--":trajpandas-0.1.1.dist-info/LICENSEPK!HڽTU >trajpandas-0.1.1.dist-info/WHEELPK!HWy#?trajpandas-0.1.1.dist-info/METADATAPK!H̓{ȭ!E@trajpandas-0.1.1.dist-info/RECORDPK 1B