def crossSpectra(data, frequency=10, notation=None, anti_aliasing=True): """ Calculates the spectrum for a set of data Parameters ---------- data: pandas.DataFrame or pandas.Series dataframe with one (will return the spectrum) or two (will return to cross-spectrum) columns frequency: float frequency of measurement of signal to pass to numpy.fft.rfftfreq anti_aliasing: bool whether or not to apply anti-aliasing according to Gobbi, Chamecki & Dias, 2006 (doi:10.1029/2005WR004374) notation: notation object notation to be used Returns -------- spectrum: pandas.DataFrame whose column is the spectrum or coespectrum of the input dataframe """ from . import notation from . import algs import numpy as np import pandas as pd from itertools import combinations notation=algs.get_notation(notation) N = len(data) combs = list(combinations(data.columns, 2)) names = [ notation.cross_spectrum % (a, b) for a, b in combs ] specs = pd.DataFrame(columns = names) #--------- # Calculate spectra here for (a, b) in combs: spec_name = notation.cross_spectrum % (a, b) spec = np.fft.rfft(data.loc[:,a]) specs.loc[:, spec_name ] = np.conj(spec) * np.fft.rfft(data.loc[:,b]) #--------- #--------- # Anti-aliasing is done here if anti_aliasing: RA = np.array([ 1. + np.cos(np.pi*k/N) for k in range(N/2+1) ])/2. specs = specs.multiply(RA**2., axis='rows') #--------- #--------- # Now we normalize the spectrum and calculate their frequency specs *= 2./(frequency*N) freq = np.fft.rfftfreq(len(data), d=1./frequency) specs.index = freq specs.index.name='Frequency' #--------- return specs
def rotate2D(data, notation=None): """Rotates the coordinates of wind data Parameters ---------- data: pandas DataFrame the dataFrame to be rotated notation: notation object a notation object to know which are the wind variables Returns ------- pandas.DataFrame the complete data input with the wind components rotated """ from math import atan2, sqrt import numpy as np import algs #------- # Getting the names for u, v, w defs = algs.get_notation(notation) wind_vars = [ defs.u, defs.v, defs.w ] #------- #------- # Definition of the coefficients used to created the rotation matrix wind_vec = data[wind_vars].mean().values m_u, m_v, m_w = wind_vec alpha = atan2(m_v,m_u) beta =-atan2(m_w, sqrt((m_u**2.)+(m_v**2.))) #------- #------- # Definition of rotation matrix DC= np.zeros((3,3)) DC[0,0],DC[0,1],DC[0,2] = np.cos(alpha)*np.cos(beta), np.cos(beta)*np.sin(alpha),-np.sin(beta) DC[1,0],DC[1,1],DC[1,2] =-np.sin(alpha) , np.cos(alpha) , 0. DC[2,0],DC[2,1],DC[2,2] = np.cos(alpha)*np.sin(beta), np.sin(alpha)*np.sin(beta), np.cos(beta) #------- #------- # Application of rotation as a matrix product data[wind_vars] = np.dot(DC, data[wind_vars].values.T).T #------- return data
def spectra(data, frequency=10, notation=None, anti_aliasing=True): """ Calculates the cross-spectra for a set of data Parameters ---------- data: pandas.DataFrame or pandas.Series dataframe with more than one columns frequency: float frequency of measurement of signal to pass to numpy.fft.rfftfreq anti_aliasing: bool whether or not to apply anti-aliasing according to Gobbi, Chamecki & Dias, 2006 (doi:10.1029/2005WR004374) Returns ------- spectra: pandas.DataFrame whose column is the spectrum or coespectrum of the input dataframe """ from . import notation from . import algs import numpy as np import pandas as pd if len(data.columns) < 2: raise TypeError('DataFrame has to has more than 1 column.') notation = algs.get_notation(notation) N = len(data) names = [ notation.spectrum % a for a in data.columns ] specs = pd.DataFrame(columns = names) #--------- # Calculate cross-spectra here for name, col in zip(names, data.columns): spec = np.fft.rfft(data.loc[:, col ]) specs.loc[:, name ] = np.conj(spec) * spec #--------- #--------- # Since it's the spectra, we can ignore the imaginary part specs = specs.apply(np.real) #--------- #--------- # Anti-aliasing is done here if anti_aliasing: RA = np.array([ 1. + np.cos(np.pi*k/N) for k in range(N/2+1) ])/2. specs = specs.multiply(RA**2., axis='rows') #--------- #--------- # Now we normalize the spectrum and calculate their frequency specs *= 2./(frequency*N) freq = np.fft.rfftfreq(len(data), d=1./frequency) specs.index = freq specs.index.name='Frequency' #--------- return specs
def detrend(data, how='linear', rule=None, notation=None, suffix=None, units=None, inplace=True, ignore=[], **kwargs): """ Returns the detrended fluctuations of a given dataset Parameters ---------- data: pandas.DataFrame, pandas.Series dataset to be detrended how: string how of average to apply. Currently {'movingmean', 'movingmedian', 'block', 'linear', 'poly'}. rule: pandas offset string the blocks for which the trends should be calculated in the block and linear type window: pandas date offset string or int if moving mean/median is chosen, this tells us the window size to pass to pandas. If int, this is the number of points used in the window. If string we will to guess the number of points from the index. Small windows (equivalent to 1min approx) work better when using rollingmedian. block_func: str, function how to resample in block type. Default is mean but it can be any numpy function that returns a float. E.g, median. degree: int degree of polynomial fit (only if how=='linear' or how=='polynomial') Returns ------- pandas.DataFrame or pandas.Series fluctuations of the input data """ from scipy import signal from . import algs import pandas as pd how=algs.stripDown(how.lower(), args='-_') df=data.copy() if ignore: df = df.drop(ignore, axis=1) defs = algs.get_notation(notation) #----------- # We can only use scipy's detrend function safely if index in not datetime if isinstance(df.index, pd.DatetimeIndex): df = df - trend(df, how=how, rule=rule, **kwargs) else: #----------- # If possible, try to use scipy's optimized functions if how=='linear' and rule==None: try: df = df.apply(signal.detrend, axis=0, type=how) except: df = df - trend(df, how=how, rule=rule, **kwargs) elif (any(w==how for w in ['block', 'blockaverage', 'blockmean'])) and (rule==None): try: df = df.apply(signal.detrend, axis=0, type='constant') except: df=df-trend(df, how=how, rule=rule, **kwargs) #----------- #----------- # If not, use our trending function else: df = df - trend(df, how=how, rule=rule, **kwargs) #----------- #----------- #----------- # We rename the columns names to indicate that they are fluctuations if units: newunits = { defs.fluctuations % el : units[el] if el in units.keys() else None for el in df.columns } #----------- #----------- # Rename the columns if suffix != '': df = df.rename(columns = lambda x: defs.fluctuations % x) #----------- #----------- # If units are not be changed in place, we copy them if units: if not inplace: units = units.copy() units.update(newunits) #----------- if inplace: return df else: return df, units