| 1 | # SpatialAnalysis/Temporal.py - Classes for temporal analysis.
|
|---|
| 2 | #
|
|---|
| 3 | # Copyright (C) 2012 Jason J. Roberts
|
|---|
| 4 | #
|
|---|
| 5 | # This program is free software; you can redistribute it and/or
|
|---|
| 6 | # modify it under the terms of the GNU General Public License
|
|---|
| 7 | # as published by the Free Software Foundation; either version 2
|
|---|
| 8 | # of the License, or (at your option) any later version.
|
|---|
| 9 | #
|
|---|
| 10 | # This program is distributed in the hope that it will be useful,
|
|---|
| 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|---|
| 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|---|
| 13 | # GNU General Public License (available in the file LICENSE.TXT)
|
|---|
| 14 | # for more details.
|
|---|
| 15 | #
|
|---|
| 16 | # You should have received a copy of the GNU General Public License
|
|---|
| 17 | # along with this program; if not, write to the Free Software
|
|---|
| 18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|---|
| 19 |
|
|---|
| 20 | import datetime
|
|---|
| 21 | import itertools
|
|---|
| 22 | import math
|
|---|
| 23 |
|
|---|
| 24 | from GeoEco.DynamicDocString import DynamicDocString
|
|---|
| 25 | from GeoEco.Internationalization import _
|
|---|
| 26 | from GeoEco.Logging import Logger
|
|---|
| 27 |
|
|---|
| 28 |
|
|---|
| 29 | class Periodicity(object):
|
|---|
| 30 | __doc__ = DynamicDocString()
|
|---|
| 31 |
|
|---|
| 32 | @classmethod
|
|---|
| 33 | def AnalyzeTable(cls, table, dateField, valueField=None, normalizationField=None, aggregationMethod=u'Mean', where=None, binWidth=None, timeUnits=None, yAxisLabel=None, scaleFactor=None, samplingPeriod=None, maxPeriod=400., periodogramMethod=u'Lomb-Scargle', oversamplingFactor=4., outputFile=None, title=None, width=4., dpi=300., dayOfYearPlot=u'Automatic', moonPhasePlot=u'Automatic', timeOfDayPlot=u'Automatic', overwriteExisting=False):
|
|---|
| 34 | cls.__doc__.Obj.ValidateMethodInvocation()
|
|---|
| 35 |
|
|---|
| 36 | # Perform additional validation.
|
|---|
| 37 |
|
|---|
| 38 | if valueField is None:
|
|---|
| 39 | normalizationField = None
|
|---|
| 40 |
|
|---|
| 41 | if normalizationField is not None and aggregationMethod == u'sum':
|
|---|
| 42 | raise ValueError(_(u'A normalization field was specified and "sum" aggregation was requested. This is not allowed. "Sum" aggregation may only be requested if a normalization field is not specified.'))
|
|---|
| 43 |
|
|---|
| 44 | if binWidth is not None and timeUnits == u'automatic':
|
|---|
| 45 | raise ValueError(_(u'A bin width was specified but the time units were not. This is not allowed. If a bin width is specified, the time units must also be specified.'))
|
|---|
| 46 |
|
|---|
| 47 | # Read the data from the table into a dictionary that maps
|
|---|
| 48 | # dates to values for those dates. If the caller's data have
|
|---|
| 49 | # high temporal precision then each date will usually have
|
|---|
| 50 | # just one value. But many datasets are not that precise--they
|
|---|
| 51 | # may have a date with no time component, for example. These
|
|---|
| 52 | # may have more than one value per date. Later, we may need
|
|---|
| 53 | # to average or sum them together, as requested by the caller.
|
|---|
| 54 |
|
|---|
| 55 | import numpy
|
|---|
| 56 |
|
|---|
| 57 | Logger.Info(_(u'Retrieving records to analyze from %(table)s.') % {u'table': table.DisplayName})
|
|---|
| 58 |
|
|---|
| 59 | countsForDates = {}
|
|---|
| 60 | valuesForDates = {}
|
|---|
| 61 | normalizeByForDates = {}
|
|---|
| 62 | minDate = None
|
|---|
| 63 | maxDate = None
|
|---|
| 64 |
|
|---|
| 65 | if where is None:
|
|---|
| 66 | cursor = table.OpenSelectCursor(rowCount=table.GetRowCount()) # Provide a value for rowCount, so the ArcGIS progressor will be used.
|
|---|
| 67 | else:
|
|---|
| 68 | cursor = table.OpenSelectCursor(where=where)
|
|---|
| 69 | try:
|
|---|
| 70 | while cursor.NextRow():
|
|---|
| 71 | date = cursor.GetValue(dateField)
|
|---|
| 72 | if date is None:
|
|---|
| 73 | continue
|
|---|
| 74 |
|
|---|
| 75 | if valueField is not None:
|
|---|
| 76 | value = cursor.GetValue(valueField)
|
|---|
| 77 | if value is None:
|
|---|
| 78 | continue
|
|---|
| 79 | value = float(value)
|
|---|
| 80 |
|
|---|
| 81 | if normalizationField is not None:
|
|---|
| 82 | normalizeBy = cursor.GetValue(normalizationField)
|
|---|
| 83 | if normalizeBy is None or normalizeBy == 0:
|
|---|
| 84 | continue
|
|---|
| 85 | normalizeBy = float(normalizeBy)
|
|---|
| 86 |
|
|---|
| 87 | if date not in countsForDates:
|
|---|
| 88 | countsForDates[date] = 1
|
|---|
| 89 | if valueField is not None:
|
|---|
| 90 | valuesForDates[date] = [value]
|
|---|
| 91 | if normalizationField is not None:
|
|---|
| 92 | normalizeByForDates[date] = [normalizeBy]
|
|---|
| 93 | else:
|
|---|
| 94 | countsForDates[date] += 1
|
|---|
| 95 | if valueField is not None:
|
|---|
| 96 | valuesForDates[date].append(value)
|
|---|
| 97 | if normalizationField is not None:
|
|---|
| 98 | normalizeByForDates[date].append(normalizeBy)
|
|---|
| 99 |
|
|---|
| 100 | if minDate is None or date < minDate:
|
|---|
| 101 | minDate = date
|
|---|
| 102 | if maxDate is None or date > maxDate:
|
|---|
| 103 | maxDate = date
|
|---|
| 104 | finally:
|
|---|
| 105 | del cursor
|
|---|
| 106 |
|
|---|
| 107 | # Validate that we got some data to analyze.
|
|---|
| 108 |
|
|---|
| 109 | if len(countsForDates) <= 0:
|
|---|
| 110 | if where is None:
|
|---|
| 111 | raise ValueError(_(u'There are no records in %(table)s to analyze.') % {u'table': table.DisplayName})
|
|---|
| 112 | raise ValueError(_(u'There were no records in %(table)s selected by the where clause %(where)s') % {u'table': table.DisplayName, u'where': where})
|
|---|
| 113 |
|
|---|
| 114 | # Build an ascending list of dates.
|
|---|
| 115 |
|
|---|
| 116 | dates = sorted(countsForDates.keys())
|
|---|
| 117 |
|
|---|
| 118 | # Determine if the dates have time components.
|
|---|
| 119 |
|
|---|
| 120 | datesHaveTimes = False
|
|---|
| 121 | for i in range(1, len(dates)):
|
|---|
| 122 | if dates[i].hour != dates[i-1].hour or dates[i].minute != dates[i-1].minute or dates[i].second != dates[i-1].second or dates[i].microsecond != dates[i-1].microsecond:
|
|---|
| 123 | datesHaveTimes = True
|
|---|
| 124 | break
|
|---|
| 125 |
|
|---|
| 126 | # If the caller did not specify a sampling period, choose 1
|
|---|
| 127 | # hour or 1 day, depending on whether or not the dates have
|
|---|
| 128 | # time components.
|
|---|
| 129 |
|
|---|
| 130 | if samplingPeriod is None:
|
|---|
| 131 | if datesHaveTimes:
|
|---|
| 132 | samplingPeriod = 1./24
|
|---|
| 133 | else:
|
|---|
| 134 | samplingPeriod = 1.
|
|---|
| 135 |
|
|---|
| 136 | # Otherwise (the caller did not specify a sampling period) and
|
|---|
| 137 | # the caller specified a sampling period of less than 1 day
|
|---|
| 138 | # but the dates do not include time components, set the
|
|---|
| 139 | # sampling period to 1 day. (Daily periodicity cannot be
|
|---|
| 140 | # detected without time components, so there is no use
|
|---|
| 141 | # checking for it. Doing so will just produce big spectral
|
|---|
| 142 | # power spikes at 1 day and its harmonics, which will be
|
|---|
| 143 | # confusing to interpret.)
|
|---|
| 144 |
|
|---|
| 145 | elif samplingPeriod < 1. and not datesHaveTimes:
|
|---|
| 146 | samplingPeriod = 1.
|
|---|
| 147 | Logger.Warning(_(u'A sampling period of less than 1 day was specified but the dates in %(dn)s do not have time components. Without times, it is not possible to detect daily periodicity and therefore there is no reason to use such a small sampling period. A sampling period of 1 day will be used instead.') % {u'dn': table.DisplayName})
|
|---|
| 148 |
|
|---|
| 149 | # Validate that the duration of the data is long enough.
|
|---|
| 150 |
|
|---|
| 151 | if minDate == maxDate:
|
|---|
| 152 | raise ValueError(_(u'All of the records have the same date (%(date)s). To analyze temporal periodicity, the records must have some variation in their dates.') % {u'date': minDate.strftime('%c')})
|
|---|
| 153 |
|
|---|
| 154 | dataDuration = maxDate - minDate
|
|---|
| 155 | dataDuration = float(dataDuration.days) + dataDuration.seconds/86400. + dataDuration.microseconds/(86400.*1000000.)
|
|---|
| 156 | if dataDuration <= samplingPeriod * 2:
|
|---|
| 157 | raise ValueError(_(u'The sampling period (%(sp)g days) is greater than one-half of the duration of the records (%(dur)g days). In order for a signal to be detected, the duration of the records must be at least twice as long as the sampling period. Please decrease the sampling period or select records that cover a longer duration.') % {u'sp': samplingPeriod, u'dur': dataDuration})
|
|---|
| 158 |
|
|---|
| 159 | # If the caller requested the Lomb-Scargle method, calculate
|
|---|
| 160 | # the periodogram using lomb_scargle.py.
|
|---|
| 161 |
|
|---|
| 162 | if periodogramMethod.lower() == u'lomb-scargle':
|
|---|
| 163 |
|
|---|
| 164 | # Build a list of values for the dates. If the caller did
|
|---|
| 165 | # not provide a value field, then we use the count of
|
|---|
| 166 | # records for the dates. If the caller did provide a value
|
|---|
| 167 | # field then we must aggregate the values that occur on
|
|---|
| 168 | # the same date as requested. Note that, unlike the FFT
|
|---|
| 169 | # method, we do not have to aggregate values from
|
|---|
| 170 | # *different* dates together in order to provide values at
|
|---|
| 171 | # a constant time increment.
|
|---|
| 172 |
|
|---|
| 173 | if valueField is None:
|
|---|
| 174 | values = [float(countsForDates[d]) for d in dates]
|
|---|
| 175 | elif aggregationMethod == u'sum':
|
|---|
| 176 | if normalizationField is None:
|
|---|
| 177 | values = [sum(valuesForDates[d]) for d in dates]
|
|---|
| 178 | else:
|
|---|
| 179 | raise ValueError(_(u'A normalization field was specified and "sum" aggregation was requested. This is not allowed. "Sum" aggregation may only be requested if a normalization field is not specified.'))
|
|---|
| 180 | elif normalizationField is None:
|
|---|
| 181 | values = [sum(valuesForDates[d]) / countsForDates[d] if countsForDates[d] > 0 else 0. for d in dates]
|
|---|
| 182 | else:
|
|---|
| 183 | values = [sum(valuesForDates[d]) / sum(normalizeByForDates[d]) if sum(normalizeByForDates[d]) != 0. else 0. for d in dates]
|
|---|
| 184 |
|
|---|
| 185 | # Convert the datetime values to the number of days
|
|---|
| 186 | # elapsed since minDate.
|
|---|
| 187 |
|
|---|
| 188 | elapsed = [float((d - minDate).days) + (d - minDate).seconds/86400. + (d - minDate).microseconds/(86400.*1000000.) for d in dates]
|
|---|
| 189 |
|
|---|
| 190 | # Calculate the periodogram.
|
|---|
| 191 |
|
|---|
| 192 | from GeoEco.AssimilatedModules.lomb_scargle import fasper
|
|---|
| 193 |
|
|---|
| 194 | frequencies, powers, Nout, Jmax, Prob = fasper(numpy.array(elapsed), numpy.array(values), oversamplingFactor, 1./samplingPeriod)
|
|---|
| 195 | periods = 1/frequencies
|
|---|
| 196 |
|
|---|
| 197 | ## # If the caller requested the fast fourier transform method,
|
|---|
| 198 | ## # calculate the periodgram using numpy.
|
|---|
| 199 | ##
|
|---|
| 200 | ## elif periodogramMethod.lower() == u'fft':
|
|---|
| 201 | ##
|
|---|
| 202 | ## # TODO: REWRITE THIS CODE. FOR NOW, I'M LEAVING FFT OUT.
|
|---|
| 203 | ##
|
|---|
| 204 | ## # Allocate a list for holding the data at the requested
|
|---|
| 205 | ## # sampling frequency, sort the data into these buckets, and
|
|---|
| 206 | ## # perform the requested aggregation.
|
|---|
| 207 | ##
|
|---|
| 208 | ## n = int(math.ceil(dataDuration / samplingPeriod))
|
|---|
| 209 | ## valueTimeSeries = [[] for i in range(n)]
|
|---|
| 210 | ##
|
|---|
| 211 | ## for i in range(len(dates)):
|
|---|
| 212 | ## elapsed = dates[i] - minDate
|
|---|
| 213 | ## elapsed = float(elapsed.days) + elapsed.seconds/86400. + elapsed.microseconds/(86400.*1000000.)
|
|---|
| 214 | ## valueTimeSeries[int(math.floor(elapsed / samplingPeriod))].append(values[i])
|
|---|
| 215 | ##
|
|---|
| 216 | ## del values, dates
|
|---|
| 217 | ##
|
|---|
| 218 | ## if aggregationMethod == u'mean':
|
|---|
| 219 | ## valueTimeSeries = [v[0] / max(1, v[1]) for v in zip([sum(v) for v in valueTimeSeries], [len(v) for v in valueTimeSeries])]
|
|---|
| 220 | ## else:
|
|---|
| 221 | ## valueTimeSeries = [sum(v) for v in valueTimeSeries]
|
|---|
| 222 | ##
|
|---|
| 223 | ## # Compute a descrete Fourier Transform for the time series of
|
|---|
| 224 | ## # values.
|
|---|
| 225 | ##
|
|---|
| 226 | ## dft = numpy.fft.rfft(valueTimeSeries)[1:] # Discard the zero-frequency term at index 0
|
|---|
| 227 | ##
|
|---|
| 228 | ## if transform is None:
|
|---|
| 229 | ## powers = abs(dft)
|
|---|
| 230 | ## elif transform == u'square':
|
|---|
| 231 | ## powers = abs(dft)**2
|
|---|
| 232 | ## elif transform == u'log':
|
|---|
| 233 | ## powers = math.log(abs(dft))
|
|---|
| 234 | ## elif transform == u'log10':
|
|---|
| 235 | ## powers = math.log10(abs(dft))
|
|---|
| 236 | ## else:
|
|---|
| 237 | ## raise ValueError(_(u'Unknown transform value.'))
|
|---|
| 238 | ##
|
|---|
| 239 | ## periods = 1. / (numpy.arange(1, len(powers) + 1, dtype='float64') )
|
|---|
| 240 |
|
|---|
| 241 | else:
|
|---|
| 242 | raise NotImplementedError(_(u'"%(method)s" is not a supported method for computing the periodogram.') % {u'method': periodogramMethod})
|
|---|
| 243 |
|
|---|
| 244 | # If the caller specified 'Automatic' for any of the radial
|
|---|
| 245 | # plots, determine whether we should plot them, based on the
|
|---|
| 246 | # temporal range and other characteristics of the data.
|
|---|
| 247 |
|
|---|
| 248 | if timeOfDayPlot == u'automatic':
|
|---|
| 249 | datesHaveTimes = False
|
|---|
| 250 | for i in range(1, len(dates)):
|
|---|
| 251 | if dates[i].hour != dates[i-1].hour or dates[i].minute != dates[i-1].minute or dates[i].second != dates[i-1].second or dates[i].microsecond != dates[i-1].microsecond:
|
|---|
| 252 | datesHaveTimes = True
|
|---|
| 253 | break
|
|---|
| 254 |
|
|---|
| 255 | dayOfYearPlot = dayOfYearPlot == u'yes' or dayOfYearPlot == u'automatic' and maxDate - minDate >= datetime.timedelta(335)
|
|---|
| 256 | moonPhasePlot = moonPhasePlot == u'yes' or moonPhasePlot == u'automatic' and maxDate - minDate >= datetime.timedelta(28)
|
|---|
| 257 | timeOfDayPlot = timeOfDayPlot == u'yes' or timeOfDayPlot == u'automatic' and maxDate - minDate >= datetime.timedelta(23./24.) and datesHaveTimes
|
|---|
| 258 |
|
|---|
| 259 | # Create the figure. Compute the height from the width and the
|
|---|
| 260 | # number of subplots.
|
|---|
| 261 |
|
|---|
| 262 | import matplotlib
|
|---|
| 263 | import matplotlib.pyplot as plt
|
|---|
| 264 | import matplotlib.gridspec as gridspec
|
|---|
| 265 | import matplotlib.lines as lines
|
|---|
| 266 |
|
|---|
| 267 | numRows = 2 + int(dayOfYearPlot or moonPhasePlot or timeOfDayPlot)
|
|---|
| 268 | numCols = max(1, int(dayOfYearPlot) + int(moonPhasePlot) + int(timeOfDayPlot))
|
|---|
| 269 |
|
|---|
| 270 | if outputFile is None:
|
|---|
| 271 | figsize = (max(6, 3 * numCols), 3 * numRows)
|
|---|
| 272 | dpi = 80
|
|---|
| 273 | else:
|
|---|
| 274 | figsize = (width, width * (float(numRows)/float(numCols)))
|
|---|
| 275 |
|
|---|
| 276 | fig = plt.figure(figsize=figsize, dpi=dpi)
|
|---|
| 277 | try:
|
|---|
| 278 | gs = gridspec.GridSpec(numRows, numCols)
|
|---|
| 279 |
|
|---|
| 280 | # Subplot 1: bar plot of values for dates.
|
|---|
| 281 | # ========================================
|
|---|
| 282 |
|
|---|
| 283 | # If the caller did not specify a bin width, select one
|
|---|
| 284 | # automatically. Our goal is to show fine resolution, so
|
|---|
| 285 | # we try to have 400 or 600 bins (we use 600 if the figure
|
|---|
| 286 | # has 3 columns) using a bin width that would be naturally
|
|---|
| 287 | # chosen by a person (e.g. "1 week", "30 days", etc).
|
|---|
| 288 |
|
|---|
| 289 | TotalSeconds = lambda dt: dt.days*86400. + dt.seconds + dt.microseconds*0.000001
|
|---|
| 290 |
|
|---|
| 291 | if binWidth is None:
|
|---|
| 292 | binWidthNames = [_(u'Hour'),
|
|---|
| 293 | _(u'4 Hours'),
|
|---|
| 294 | _(u'6 Hours'),
|
|---|
| 295 | _(u'12 Hours'),
|
|---|
| 296 | _(u'Day'),
|
|---|
| 297 | _(u'3 Days'),
|
|---|
| 298 | _(u'Week'),
|
|---|
| 299 | _(u'14 Days'),
|
|---|
| 300 | _(u'30 Days'),
|
|---|
| 301 | _(u'90 Days'),
|
|---|
| 302 | _(u'Year')]
|
|---|
| 303 |
|
|---|
| 304 | binWidthDeltas = [datetime.timedelta(seconds=3600),
|
|---|
| 305 | datetime.timedelta(seconds=3600*4),
|
|---|
| 306 | datetime.timedelta(seconds=3600*6),
|
|---|
| 307 | datetime.timedelta(seconds=3600*12),
|
|---|
| 308 | datetime.timedelta(days=1),
|
|---|
| 309 | datetime.timedelta(days=3),
|
|---|
| 310 | datetime.timedelta(days=7),
|
|---|
| 311 | datetime.timedelta(days=14),
|
|---|
| 312 | datetime.timedelta(days=30),
|
|---|
| 313 | datetime.timedelta(days=90),
|
|---|
| 314 | datetime.timedelta(days=365)]
|
|---|
| 315 |
|
|---|
| 316 | if numCols <= 2:
|
|---|
| 317 | desiredBins = 400
|
|---|
| 318 | else:
|
|---|
| 319 | desiredBins = 600
|
|---|
| 320 |
|
|---|
| 321 | numBinsForDeltas = [TotalSeconds(maxDate - minDate) / TotalSeconds(delta) < desiredBins for delta in binWidthDeltas]
|
|---|
| 322 |
|
|---|
| 323 | if True not in numBinsForDeltas:
|
|---|
| 324 | binWidthInSeconds = TotalSeconds(binWidthDeltas[-1])
|
|---|
| 325 | binWidthName = binWidthNames[-1]
|
|---|
| 326 | else:
|
|---|
| 327 | binWidthInSeconds = TotalSeconds(binWidthDeltas[numBinsForDeltas.index(True)])
|
|---|
| 328 | binWidthName = binWidthNames[numBinsForDeltas.index(True)]
|
|---|
| 329 |
|
|---|
| 330 | # Otherwise (the caller did specify a bin width), create a
|
|---|
| 331 | # display name for it and compute its width in seconds.
|
|---|
| 332 |
|
|---|
| 333 | else:
|
|---|
| 334 | if timeUnits == u'hours':
|
|---|
| 335 | binWidthInSeconds = TotalSeconds(datetime.timedelta(hours=binWidth))
|
|---|
| 336 | if binWidthInSeconds == 3600:
|
|---|
| 337 | binWidthName = 'Hour'
|
|---|
| 338 | elif binWidthInSeconds > 3600:
|
|---|
| 339 | binWidthName = '%r Hours' % round((binWidthInSeconds / 3600), 1)
|
|---|
| 340 | else:
|
|---|
| 341 | binWidthName = '%r Hour' % round((binWidthInSeconds / 3600), 2)
|
|---|
| 342 |
|
|---|
| 343 | elif timeUnits == u'days':
|
|---|
| 344 | binWidthInSeconds = TotalSeconds(datetime.timedelta(binWidth))
|
|---|
| 345 | if binWidthInSeconds == 86400*365:
|
|---|
| 346 | binWidthName = 'Year'
|
|---|
| 347 | elif binWidthInSeconds == 86400*7:
|
|---|
| 348 | binWidthName = 'Week'
|
|---|
| 349 | elif binWidthInSeconds == 86400:
|
|---|
| 350 | binWidthName = 'Day'
|
|---|
| 351 | elif binWidthInSeconds > 86400:
|
|---|
| 352 | binWidthName = '%r Days' % round((binWidthInSeconds / 3600), 1)
|
|---|
| 353 | else:
|
|---|
| 354 | binWidthName = '%r Day' % round((binWidthInSeconds / 3600), 2)
|
|---|
| 355 |
|
|---|
| 356 | else:
|
|---|
| 357 | raise ValueError(_(u'Programming error in this tool: Unknown timeUnits %r. Please contact the author of this tool for assistance.') % timeUnits)
|
|---|
| 358 |
|
|---|
| 359 | # Determine the start date for the first bin. For display
|
|---|
| 360 | # purposes, we want this to be rounded to a whole hour,
|
|---|
| 361 | # day, or year.
|
|---|
| 362 |
|
|---|
| 363 | if binWidthName.endswith('Hour') or binWidthName.endswith('Hours'):
|
|---|
| 364 | firstBinStartDate = datetime.datetime(minDate.year, minDate.month, minDate.day, minDate.hour)
|
|---|
| 365 | elif binWidthName.endswith('Year'):
|
|---|
| 366 | firstBinStartDate = datetime.datetime(minDate.year, 1, 1)
|
|---|
| 367 | else:
|
|---|
| 368 | firstBinStartDate = datetime.datetime(minDate.year, minDate.month, minDate.day)
|
|---|
| 369 |
|
|---|
| 370 | # Define a function that returns the start date of the bin
|
|---|
| 371 | # that a given date falls within.
|
|---|
| 372 |
|
|---|
| 373 | BinStartDate = lambda d: firstBinStartDate + datetime.timedelta(seconds=math.floor(TotalSeconds(d - firstBinStartDate) / binWidthInSeconds) * binWidthInSeconds)
|
|---|
| 374 |
|
|---|
| 375 | # Calculate the values of the bins.
|
|---|
| 376 |
|
|---|
| 377 | binStartDates = []
|
|---|
| 378 | binValues = []
|
|---|
| 379 |
|
|---|
| 380 | for binStartDate, datesInBin in itertools.groupby(dates, BinStartDate):
|
|---|
| 381 | binStartDates.append(binStartDate)
|
|---|
| 382 |
|
|---|
| 383 | if valueField is None:
|
|---|
| 384 | binValues.append(sum([countsForDates[d] for d in datesInBin]))
|
|---|
| 385 |
|
|---|
| 386 | elif aggregationMethod == u'sum':
|
|---|
| 387 | if normalizationField is None:
|
|---|
| 388 | binValues.append(sum([sum(valuesForDates[d]) for d in datesInBin]))
|
|---|
| 389 | else:
|
|---|
| 390 | raise ValueError(_(u'A normalization field was specified and "sum" aggregation was requested. This is not allowed. "Sum" aggregation may only be requested if a normalization field is not specified.'))
|
|---|
| 391 |
|
|---|
| 392 | else:
|
|---|
| 393 | datesInBinList = list(datesInBin) # This is necessary because datesInBin is an iterator and we need to access it multiple times
|
|---|
| 394 | sumOfValues = float(sum([sum(valuesForDates[d]) for d in datesInBinList]))
|
|---|
| 395 |
|
|---|
| 396 | if normalizationField is None:
|
|---|
| 397 | count = sum([countsForDates[d] for d in datesInBinList])
|
|---|
| 398 | if count > 0:
|
|---|
| 399 | binValues.append(sumOfValues / count)
|
|---|
| 400 | else:
|
|---|
| 401 | binValues.append(0.)
|
|---|
| 402 |
|
|---|
| 403 | else:
|
|---|
| 404 | sumOfNormalizeBy = float(sum([sum(normalizeByForDates[d]) for d in datesInBinList]))
|
|---|
| 405 | if sumOfNormalizeBy > 0:
|
|---|
| 406 | binValues.append(sumOfValues / sumOfNormalizeBy)
|
|---|
| 407 | else:
|
|---|
| 408 | binValues.append(0.)
|
|---|
| 409 |
|
|---|
| 410 | # If the caller provided a scale factor, apply it.
|
|---|
| 411 |
|
|---|
| 412 | if scaleFactor is not None:
|
|---|
| 413 | binValues = [value * scaleFactor for value in binValues]
|
|---|
| 414 | scaleFactorLabel = u' \xd7 %g' % scaleFactor
|
|---|
| 415 | else:
|
|---|
| 416 | scaleFactorLabel = u''
|
|---|
| 417 |
|
|---|
| 418 | # Now create the plot
|
|---|
| 419 |
|
|---|
| 420 | matplotlib.rc('xtick', direction='out')
|
|---|
| 421 | matplotlib.rc('ytick', direction='out')
|
|---|
| 422 |
|
|---|
| 423 | ax1 = plt.subplot(gs[0, :])
|
|---|
| 424 | ax1.bar(binStartDates, binValues, width=binWidthInSeconds / 86400., color='black', linewidth=0) # Convert seconds to days, which matplotlib uses as width of bars when x axis uses datetimes
|
|---|
| 425 |
|
|---|
| 426 | if yAxisLabel is not None:
|
|---|
| 427 | ax1.set_ylabel(yAxisLabel)
|
|---|
| 428 | elif valueField is None:
|
|---|
| 429 | ax1.set_ylabel(u'Count / ' + binWidthName + scaleFactorLabel)
|
|---|
| 430 | elif aggregationMethod == u'sum':
|
|---|
| 431 | if normalizationField is None:
|
|---|
| 432 | ax1.set_ylabel(valueField + u' / ' + binWidthName + scaleFactorLabel)
|
|---|
| 433 | else:
|
|---|
| 434 | raise ValueError(_(u'A normalization field was specified and "sum" aggregation was requested. This is not allowed. "Sum" aggregation may only be requested if a normalization field is not specified.'))
|
|---|
| 435 | elif normalizationField is None:
|
|---|
| 436 | ax1.set_ylabel(u'Mean ' + valueField + scaleFactorLabel)
|
|---|
| 437 | else:
|
|---|
| 438 | ax1.set_ylabel(valueField + u' / ' + normalizationField + scaleFactorLabel)
|
|---|
| 439 |
|
|---|
| 440 | # Rotate the dates on the x axis to make them easier to
|
|---|
| 441 | # read. This code was adapted from matplotlib's
|
|---|
| 442 | # autofmt_xdate().
|
|---|
| 443 |
|
|---|
| 444 | for label in ax1.get_xticklabels():
|
|---|
| 445 | label.set_ha('right')
|
|---|
| 446 | label.set_rotation(30)
|
|---|
| 447 |
|
|---|
| 448 | # Subplot 2: the periodogram.
|
|---|
| 449 | # ===========================
|
|---|
| 450 |
|
|---|
| 451 | periodsToPlot = numpy.logical_and(periods >= samplingPeriod * 2, periods <= min(maxPeriod, max(periods)))
|
|---|
| 452 |
|
|---|
| 453 | ax2 = plt.subplot(gs[1, :])
|
|---|
| 454 | plt.plot(periods[periodsToPlot], powers[periodsToPlot], 'k')
|
|---|
| 455 | ax2.set_xlabel('Period (days)')
|
|---|
| 456 | ax2.set_ylabel('Spectral Power')
|
|---|
| 457 | #ax2.set_xlim(right=min(maxPeriod, max(periods)))
|
|---|
| 458 |
|
|---|
| 459 | matplotlib.rc('xtick', direction='in')
|
|---|
| 460 | matplotlib.rc('ytick', direction='in')
|
|---|
| 461 |
|
|---|
| 462 | # Subplot 3: day-of-year radial bar plot.
|
|---|
| 463 | # =======================================
|
|---|
| 464 |
|
|---|
| 465 | if dayOfYearPlot:
|
|---|
| 466 |
|
|---|
| 467 | # Calculate the 0-indexed day of the year as 0 to 364
|
|---|
| 468 | # (Jan 1 to Dec 31). On leap years, treat both Dec 30
|
|---|
| 469 | # and 31 as day 364.
|
|---|
| 470 |
|
|---|
| 471 | daysOfYear = [min(364, int(d.strftime('%j')) - 1) for d in dates]
|
|---|
| 472 |
|
|---|
| 473 | # Classify the moon phase values into 73 bins, each 5
|
|---|
| 474 | # days long.
|
|---|
| 475 |
|
|---|
| 476 | binForDOY = [int(math.floor(doy / 5.)) for doy in daysOfYear]
|
|---|
| 477 |
|
|---|
| 478 | # Compute the mean value for each bin.
|
|---|
| 479 |
|
|---|
| 480 | binCount = numpy.zeros(73, int)
|
|---|
| 481 | binSum = numpy.zeros(len(binCount))
|
|---|
| 482 |
|
|---|
| 483 | for i in range(len(values)):
|
|---|
| 484 | binCount[binForDOY[i]] += 1
|
|---|
| 485 | binSum[binForDOY[i]] += values[i]
|
|---|
| 486 |
|
|---|
| 487 | binMean = numpy.zeros(len(binCount))
|
|---|
| 488 | binMean[binCount > 0] = binSum[binCount > 0] / binCount[binCount > 0]
|
|---|
| 489 |
|
|---|
| 490 | # Normalize the means.
|
|---|
| 491 |
|
|---|
| 492 | binMean /= binSum.sum()
|
|---|
| 493 |
|
|---|
| 494 | # Create a polar bar plot.
|
|---|
| 495 |
|
|---|
| 496 | ax3 = plt.subplot(gs[2, 0], polar=True)
|
|---|
| 497 | ax3.bar([float(i)/len(binCount) * 2 * math.pi for i in range(len(binCount))], binMean, 1./len(binCount) * 2 * math.pi, color='gray')
|
|---|
| 498 | ax3.set_yticklabels([])
|
|---|
| 499 |
|
|---|
| 500 | # Label the first days of the months.
|
|---|
| 501 |
|
|---|
| 502 | ax3.set_theta_zero_location('S')
|
|---|
| 503 | ax3.set_theta_direction(-1)
|
|---|
| 504 |
|
|---|
| 505 | ax3.set_thetagrids([(int(datetime.datetime(2001, month, 1).strftime('%j')) - 1)/365. * 360. for month in range(1, 13)],
|
|---|
| 506 | ['Jan 1', 'Feb 1', 'Mar 1', 'Apr 1', 'May 1', 'Jun 1', 'Jul 1', 'Aug 1', 'Sep 1', 'Oct 1', 'Nov 1', 'Dec 1']);
|
|---|
| 507 |
|
|---|
| 508 | for label in ax3.get_xticklabels():
|
|---|
| 509 | if label.get_text() == 'Jan 1':
|
|---|
| 510 | label.set_va('top')
|
|---|
| 511 | elif label.get_text() in ['Feb 1', 'Mar 1', 'Apr 1', 'May 1', 'Jun 1']:
|
|---|
| 512 | label.set_ha('right')
|
|---|
| 513 | elif label.get_text() == 'Jul 1':
|
|---|
| 514 | label.set_va('bottom')
|
|---|
| 515 | else:
|
|---|
| 516 | label.set_ha('left')
|
|---|
| 517 |
|
|---|
| 518 | # Subplot 4: moon-phase radial bar plot.
|
|---|
| 519 | # ======================================
|
|---|
| 520 |
|
|---|
| 521 | if moonPhasePlot:
|
|---|
| 522 |
|
|---|
| 523 | # Calculate the moon phase as a number ranging from 0
|
|---|
| 524 | # to 1, where 0 and 1 are new moon and 0.5 is full.
|
|---|
| 525 |
|
|---|
| 526 | from GeoEco.AssimilatedModules.moon import MoonPhase
|
|---|
| 527 |
|
|---|
| 528 | mp = [MoonPhase(d).phase for d in dates]
|
|---|
| 529 |
|
|---|
| 530 | # Classify the moon phase values into 30 bins, each
|
|---|
| 531 | # just under 1 day long.
|
|---|
| 532 |
|
|---|
| 533 | binForMP = [int(math.floor(p * 30.)) for p in mp]
|
|---|
| 534 |
|
|---|
| 535 | # Compute the mean value for each bin.
|
|---|
| 536 |
|
|---|
| 537 | binCount = numpy.zeros(30, int)
|
|---|
| 538 | binSum = numpy.zeros(len(binCount))
|
|---|
| 539 |
|
|---|
| 540 | for i in range(len(values)):
|
|---|
| 541 | binCount[binForMP[i]] += 1
|
|---|
| 542 | binSum[binForMP[i]] += values[i]
|
|---|
| 543 |
|
|---|
| 544 | binMean = numpy.zeros(len(binCount))
|
|---|
| 545 | binMean[binCount > 0] = binSum[binCount > 0] / binCount[binCount > 0]
|
|---|
| 546 |
|
|---|
| 547 | # Normalize the means.
|
|---|
| 548 |
|
|---|
| 549 | binMean /= binSum.sum()
|
|---|
| 550 |
|
|---|
| 551 | # Create a polar bar plot.
|
|---|
| 552 |
|
|---|
| 553 | ax4 = plt.subplot(gs[2, 0 + int(dayOfYearPlot)], polar=True)
|
|---|
| 554 | ax4.bar([float(i)/len(binCount) * 2 * math.pi for i in range(len(binCount))], binMean, 1./len(binCount) * 2 * math.pi, color='gray')
|
|---|
| 555 | ax4.set_yticklabels([])
|
|---|
| 556 |
|
|---|
| 557 | # Label the quadrants with moon phase names.
|
|---|
| 558 |
|
|---|
| 559 | ax4.set_theta_zero_location('W')
|
|---|
| 560 | ax4.set_theta_direction(-1)
|
|---|
| 561 |
|
|---|
| 562 | ax4.set_thetagrids([0, 90, 180, 270], ['New\nMoon', 'First Quarter', 'Full\nMoon', 'Last Quarter']);
|
|---|
| 563 |
|
|---|
| 564 | for label in ax4.get_xticklabels():
|
|---|
| 565 | if label.get_text() == 'New\nMoon':
|
|---|
| 566 | label.set_ha('right')
|
|---|
| 567 | elif label.get_text() == 'First Quarter':
|
|---|
| 568 | label.set_va('bottom')
|
|---|
| 569 | elif label.get_text() == 'Full\nMoon':
|
|---|
| 570 | label.set_ha('left')
|
|---|
| 571 | else:
|
|---|
| 572 | label.set_va('top')
|
|---|
| 573 |
|
|---|
| 574 | # Subplot 5: day-of-year radial bar plot.
|
|---|
| 575 | # =======================================
|
|---|
| 576 |
|
|---|
| 577 | if timeOfDayPlot:
|
|---|
| 578 |
|
|---|
| 579 | # Extract the hours from the datetime instances.
|
|---|
| 580 |
|
|---|
| 581 | hours = [d.hour for d in dates]
|
|---|
| 582 |
|
|---|
| 583 | # Compute the mean value for each bin.
|
|---|
| 584 |
|
|---|
| 585 | binCount = numpy.zeros(24, int)
|
|---|
| 586 | binSum = numpy.zeros(len(binCount))
|
|---|
| 587 |
|
|---|
| 588 | for i in range(len(values)):
|
|---|
| 589 | binCount[hours[i]] += 1
|
|---|
| 590 | binSum[hours[i]] += values[i]
|
|---|
| 591 |
|
|---|
| 592 | binMean = numpy.zeros(len(binCount))
|
|---|
| 593 | binMean[binCount > 0] = binSum[binCount > 0] / binCount[binCount > 0]
|
|---|
| 594 |
|
|---|
| 595 | # Normalize the means.
|
|---|
| 596 |
|
|---|
| 597 | binMean /= binSum.sum()
|
|---|
| 598 |
|
|---|
| 599 | # Create a polar bar plot.
|
|---|
| 600 |
|
|---|
| 601 | ax5 = plt.subplot(gs[2, -1], polar=True)
|
|---|
| 602 | ax5.bar([float(i)/len(binCount) * 2 * math.pi for i in range(len(binCount))], binMean, 1./len(binCount) * 2 * math.pi, color='gray')
|
|---|
| 603 | ax5.set_yticklabels([])
|
|---|
| 604 |
|
|---|
| 605 | # Label the first days of the months.
|
|---|
| 606 |
|
|---|
| 607 | ax5.set_theta_zero_location('S')
|
|---|
| 608 | ax5.set_theta_direction(-1)
|
|---|
| 609 |
|
|---|
| 610 | ax5.set_thetagrids([360 * hour/24. for hour in range(0, 24, 3)],
|
|---|
| 611 | ['%02i:00' % hour for hour in range(0, 24, 3)]);
|
|---|
| 612 |
|
|---|
| 613 | for label in ax5.get_xticklabels():
|
|---|
| 614 | if label.get_text() == '00:00':
|
|---|
| 615 | label.set_va('top')
|
|---|
| 616 | elif label.get_text() in ['03:00', '06:00', '09:00']:
|
|---|
| 617 | label.set_ha('right')
|
|---|
| 618 | elif label.get_text() == '12:00':
|
|---|
| 619 | label.set_va('bottom')
|
|---|
| 620 | else:
|
|---|
| 621 | label.set_ha('left')
|
|---|
| 622 |
|
|---|
| 623 | # Use tight layout to allow dates to display properly.
|
|---|
| 624 |
|
|---|
| 625 | plt.tight_layout()
|
|---|
| 626 |
|
|---|
| 627 | # If the caller provided a plot title, add it. This must
|
|---|
| 628 | # be done after calling plt.tight_layout().
|
|---|
| 629 |
|
|---|
| 630 | if title is not None:
|
|---|
| 631 | plt.subplots_adjust(top=0.93)
|
|---|
| 632 | fig.suptitle(title)
|
|---|
| 633 |
|
|---|
| 634 | # If the caller provided an outputFile, save the figure to
|
|---|
| 635 | # it. If not, display the figure interactively.
|
|---|
| 636 |
|
|---|
| 637 | if outputFile is not None:
|
|---|
| 638 | plt.savefig(outputFile)
|
|---|
| 639 | else:
|
|---|
| 640 | Logger.Info(_(u'Waiting for the interactive plot window to be closed...'))
|
|---|
| 641 | plt.show()
|
|---|
| 642 |
|
|---|
| 643 | finally:
|
|---|
| 644 | plt.close(fig)
|
|---|
| 645 |
|
|---|
| 646 | @classmethod
|
|---|
| 647 | def AnalyzeArcGISTable(cls, table, dateField, valueField=None, normalizationField=None, aggregationMethod=u'Mean', where=None, binWidth=None, timeUnits=None, yAxisLabel=None, scaleFactor=None, samplingPeriod=None, maxPeriod=400., periodogramMethod=u'Lomb-Scargle', oversamplingFactor=4., outputFile=None, title=None, width=4., dpi=300., dayOfYearPlot=u'Automatic', moonPhasePlot=u'Automatic', timeOfDayPlot=u'Automatic', overwriteExisting=False):
|
|---|
| 648 | cls.__doc__.Obj.ValidateMethodInvocation()
|
|---|
| 649 | from GeoEco.Datasets.ArcGIS import ArcGISTable
|
|---|
| 650 | cls.AnalyzeTable(ArcGISTable(table), dateField, valueField, normalizationField, aggregationMethod, where, binWidth, timeUnits, yAxisLabel, scaleFactor, samplingPeriod, maxPeriod, periodogramMethod, oversamplingFactor, outputFile, title, width, dpi, dayOfYearPlot, moonPhasePlot, timeOfDayPlot, overwriteExisting)
|
|---|
| 651 |
|
|---|
| 652 |
|
|---|
| 653 | ###############################################################################
|
|---|
| 654 | # Metadata: module
|
|---|
| 655 | ###############################################################################
|
|---|
| 656 |
|
|---|
| 657 | from GeoEco.ArcGIS import ArcGISDependency
|
|---|
| 658 | from GeoEco.Datasets import Table
|
|---|
| 659 | from GeoEco.Dependencies import MatplotlibDependency
|
|---|
| 660 | from GeoEco.Metadata import *
|
|---|
| 661 | from GeoEco.Types import *
|
|---|
| 662 |
|
|---|
| 663 | AddModuleMetadata(shortDescription=_(u'Classes for temporal analysis.'))
|
|---|
| 664 |
|
|---|
| 665 | ###############################################################################
|
|---|
| 666 | # Metadata: Periodicity class
|
|---|
| 667 | ###############################################################################
|
|---|
| 668 |
|
|---|
| 669 | AddClassMetadata(Periodicity,
|
|---|
| 670 | shortDescription=_(u'Methods for analyzing temporal perodicity in time-series data.'),
|
|---|
| 671 | isExposedAsCOMServer=True,
|
|---|
| 672 | comIID=u'{CABE75DC-A0A4-410D-8698-9FEF7085232A}',
|
|---|
| 673 | comCLSID=u'{8D268D4D-320B-4E07-8948-954D90F87544}')
|
|---|
| 674 |
|
|---|
| 675 | # Public method: Periodicity.AnalyzeTable
|
|---|
| 676 |
|
|---|
| 677 | AddMethodMetadata(Periodicity.AnalyzeTable,
|
|---|
| 678 | shortDescription=_(u'Creates several plots that show the temporal periodicity of the records in a Table.'),
|
|---|
| 679 | longDescription=_(
|
|---|
| 680 | u"""This tool creates several plots that show whether or not the
|
|---|
| 681 | records or the value of some field of them exhibit any temporal
|
|---|
| 682 | periodicity (i.e., patterns that repeat in time). What is analyzed and
|
|---|
| 683 | presented in the plots depends on what fields you select, if any, for
|
|---|
| 684 | analysis.
|
|---|
| 685 |
|
|---|
| 686 | The first plot is a bar plot showing values at different times over
|
|---|
| 687 | the temporal extent of the data. If you do not select a Value Field
|
|---|
| 688 | for analysis, then this plot will be a simple histogram showing the
|
|---|
| 689 | counts of records in a series of equal-duration time bins. If you do
|
|---|
| 690 | select a Value Field, then the plot will show the sum or mean of that
|
|---|
| 691 | field for each bin (the Aggregation Method parameter specifies whether
|
|---|
| 692 | the sum or mean is used). Finally, if you also specify a Normalization
|
|---|
| 693 | Field, the plot will show the sum of the Value Field divided by the
|
|---|
| 694 | sum of the Normalization Field, for each bin (the Aggregation Method
|
|---|
| 695 | parameter is ignored in this case).
|
|---|
| 696 |
|
|---|
| 697 | The second plot is a periodogram that indicates whether or not a
|
|---|
| 698 | repeating signal exists in the data at various temporal periods. The
|
|---|
| 699 | second plot is produced by conducting Fourier analysis on the data
|
|---|
| 700 | plotted in the first plot. In Fourier analysis, the overall pattern in
|
|---|
| 701 | the data is decomposed into a series of sine waves having different
|
|---|
| 702 | periods (or frequencies), which accurately reconstruct the overall
|
|---|
| 703 | pattern when summed together. The periodogram summarizes the
|
|---|
| 704 | collection of sine waves by plotting a point for each one; the x axis
|
|---|
| 705 | is the period of the sine wave (the inverse of its frequency) and the
|
|---|
| 706 | y axis is its amplitude.
|
|---|
| 707 |
|
|---|
| 708 | Sine waves with high amplitudes represent strong cyclic patterns
|
|---|
| 709 | present in the overall pattern. These appear as peaks in the
|
|---|
| 710 | periodogram. In marine ecological data, patterns are often observed in
|
|---|
| 711 | relation to solar, lunar, or tidal cycles. In the periodogram, solar
|
|---|
| 712 | cycles are indicated by peaks at 365 days (an annual cycle) and at 24
|
|---|
| 713 | hours (a diurnal cycle). Lunar cycles are indicated by a peak at 29.5
|
|---|
| 714 | days. Tidal cycles are more complicated to observe, and depend on
|
|---|
| 715 | tidal patterns in the region of interest.
|
|---|
| 716 |
|
|---|
| 717 | To interpret this periodogram, look for high spikes at 365 days, 29.5
|
|---|
| 718 | days, 1 day, with very low values elsewhere. If you see high spikes at
|
|---|
| 719 | one or more of those locations, a solar or lunar cycle is indicated.
|
|---|
| 720 | But if those spikes are not very high relative to other periods--if
|
|---|
| 721 | the periodogram displays a generally jittery or noisy pattern--then
|
|---|
| 722 | solar and lunar cycles may not exist.
|
|---|
| 723 |
|
|---|
| 724 | In order for Fourier analysis to detect such patterns, the data must
|
|---|
| 725 | span at least two cycles. For example, to detect an annual pattern,
|
|---|
| 726 | the data must span at least two years. The results will be much more
|
|---|
| 727 | reliable if many years of data are available.
|
|---|
| 728 |
|
|---|
| 729 | To better determine whether annual, lunar, or diurnal patterns exist,
|
|---|
| 730 | up to three optional plots will be created. Each of these is a radial
|
|---|
| 731 | bar plot, similar to the first bar plot but binning the data according
|
|---|
| 732 | to day of the year, phase of the lunar cycle, or hour of the day. If
|
|---|
| 733 | temporal cycles exist, the radial plots will show high values at some
|
|---|
| 734 | times and low values at others.
|
|---|
| 735 |
|
|---|
| 736 | By default, all three radial plots will be produced if there are
|
|---|
| 737 | sufficient data available, but you can manually enable or disable each
|
|---|
| 738 | one."""),
|
|---|
| 739 | isExposedToPythonCallers=True,
|
|---|
| 740 | dependencies=[MatplotlibDependency()])
|
|---|
| 741 |
|
|---|
| 742 | AddArgumentMetadata(Periodicity.AnalyzeTable, u'cls',
|
|---|
| 743 | typeMetadata=ClassOrClassInstanceTypeMetadata(cls=Periodicity),
|
|---|
| 744 | description=_(u'Periodicity class or instance.'))
|
|---|
| 745 |
|
|---|
| 746 | AddArgumentMetadata(Periodicity.AnalyzeTable, u'table',
|
|---|
| 747 | typeMetadata=ClassInstanceTypeMetadata(cls=Table),
|
|---|
| 748 | description=_(
|
|---|
| 749 | u"""Table to analyze.
|
|---|
| 750 |
|
|---|
| 751 | The table must contain, at minimum, a date field. It may optionally
|
|---|
| 752 | contain an integer or floating point field of values to analyze, and
|
|---|
| 753 | another integer or floating point field used to normalize the
|
|---|
| 754 | values."""))
|
|---|
| 755 |
|
|---|
| 756 | AddArgumentMetadata(Periodicity.AnalyzeTable, u'dateField',
|
|---|
| 757 | typeMetadata=TableFieldTypeMetadata(u'table', allowedFieldTypes=[u'date', u'datetime']),
|
|---|
| 758 | description=_(
|
|---|
| 759 | u"""Field containing the dates of the records."""))
|
|---|
| 760 |
|
|---|
| 761 | AddArgumentMetadata(Periodicity.AnalyzeTable, u'valueField',
|
|---|
| 762 | typeMetadata=TableFieldTypeMetadata(u'table', allowedFieldTypes=[u'int8', u'uint8', u'int16', u'uint16', u'int32', u'uint32', u'int64', u'uint64', u'float32', u'float64'], canBeNone=True),
|
|---|
| 763 | description=_(
|
|---|
| 764 | u"""Field containing the values to analyze.
|
|---|
| 765 |
|
|---|
| 766 | If this field is not provided, the temporal analysis will be conducted
|
|---|
| 767 | only on the dates of the records. The first plot will be a simple
|
|---|
| 768 | histogram showing the counts of records for a series of equal-duration
|
|---|
| 769 | time bins. The periodogram will be based on a Fourier analysis of the
|
|---|
| 770 | counts.
|
|---|
| 771 |
|
|---|
| 772 | If this field is provided and the Normalization Field is not provided,
|
|---|
| 773 | the temporal analysis will be conducted on the values of this field
|
|---|
| 774 | for the dates they occur. The first plot will show the sum or mean of
|
|---|
| 775 | the values for each time bin (the Aggregation Method parameter
|
|---|
| 776 | specifies whether the sum or mean is used). The periodogram will be
|
|---|
| 777 | based on a Fourier analysis of the values.
|
|---|
| 778 |
|
|---|
| 779 | If this field is provided and the Normalization Field is also
|
|---|
| 780 | provided, the first plot will show the sum of values of this field
|
|---|
| 781 | divided by the sum of the values of the Normalization Field, for each
|
|---|
| 782 | time bin (the Aggregation Method parameter is ignored in this case).
|
|---|
| 783 | The periodogram will be based on a Fourier analysis of the normalized
|
|---|
| 784 | values."""))
|
|---|
| 785 |
|
|---|
| 786 | AddArgumentMetadata(Periodicity.AnalyzeTable, u'normalizationField',
|
|---|
| 787 | typeMetadata=TableFieldTypeMetadata(u'table', allowedFieldTypes=[u'int8', u'uint8', u'int16', u'uint16', u'int32', u'uint32', u'int64', u'uint64', u'float32', u'float64'], mustBeDifferentThanArguments=[u'valueField'], canBeNone=True),
|
|---|
| 788 | description=_(
|
|---|
| 789 | u"""Field for normalizing the Value Field. Please see the
|
|---|
| 790 | documentation for that parameter for more information.
|
|---|
| 791 |
|
|---|
| 792 | The Normalization Field is ignored if the Value Field is not
|
|---|
| 793 | provided."""))
|
|---|
| 794 |
|
|---|
| 795 | AddArgumentMetadata(Periodicity.AnalyzeTable, u'aggregationMethod',
|
|---|
| 796 | typeMetadata=UnicodeStringTypeMetadata(allowedValues=[u'Mean', u'Sum'], makeLowercase=True),
|
|---|
| 797 | description=_(
|
|---|
| 798 | u"""Specifies whether the analysis should use the mean or the sum of
|
|---|
| 799 | the Value Field. Please see the documentation for that parameter for
|
|---|
| 800 | more information.
|
|---|
| 801 |
|
|---|
| 802 | If a Value Field is not specified, this parameter is ignored. If a
|
|---|
| 803 | Value Field and Normalization Field both are provided, then this
|
|---|
| 804 | parameter must be set to Mean."""),
|
|---|
| 805 | arcGISDisplayName=_(u'Aggregation method'))
|
|---|
| 806 |
|
|---|
| 807 | AddArgumentMetadata(Periodicity.AnalyzeTable, u'where',
|
|---|
| 808 | typeMetadata=UnicodeStringTypeMetadata(canBeNone=True),
|
|---|
| 809 | description=_(
|
|---|
| 810 | u"""SQL WHERE clause expression that specifies the subset of points to
|
|---|
| 811 | use. If this parameter is not provided, all of the points will be
|
|---|
| 812 | used. The exact syntax of this expression depends on the underlying
|
|---|
| 813 | storage format of the input Table."""))
|
|---|
| 814 |
|
|---|
| 815 | AddArgumentMetadata(Periodicity.AnalyzeTable, u'binWidth',
|
|---|
| 816 | typeMetadata=FloatTypeMetadata(mustBeGreaterThan=0., canBeNone=True),
|
|---|
| 817 | description=_(
|
|---|
| 818 | u"""Width, in time units, of the bins used to produce the first plot.
|
|---|
| 819 | If you provide a value for this parmaeter, you must also specify the
|
|---|
| 820 | value for the Time Units parameter.
|
|---|
| 821 |
|
|---|
| 822 | If this parameter is omitted, a value that allows a large number of
|
|---|
| 823 | bins will be automatically selected, with the hope that fine temporal
|
|---|
| 824 | dynamics may be observable."""),
|
|---|
| 825 | arcGISDisplayName=_(u'Bar plot bin width'),
|
|---|
| 826 | arcGISCategory=_(u'Bar plot options'))
|
|---|
| 827 |
|
|---|
| 828 | AddArgumentMetadata(Periodicity.AnalyzeTable, u'timeUnits',
|
|---|
| 829 | typeMetadata=UnicodeStringTypeMetadata(allowedValues=[u'Days', u'Hours'], makeLowercase=True, canBeNone=True),
|
|---|
| 830 | description=_(
|
|---|
| 831 | u"""Specifies the time units of the Bar Plot Bin Width parameter. If
|
|---|
| 832 | you specify a Bar Plot Bin Width, you must also specify the time
|
|---|
| 833 | units."""),
|
|---|
| 834 | arcGISDisplayName=_(u'Time units'),
|
|---|
| 835 | arcGISCategory=_(u'Bar plot options'))
|
|---|
| 836 |
|
|---|
| 837 | AddArgumentMetadata(Periodicity.AnalyzeTable, u'yAxisLabel',
|
|---|
| 838 | typeMetadata=UnicodeStringTypeMetadata(canBeNone=True),
|
|---|
| 839 | description=_(
|
|---|
| 840 | u"""Label for the y axis of the bar plot. If you do not provide a
|
|---|
| 841 | label, a generic label will be created for you."""),
|
|---|
| 842 | arcGISDisplayName=_(u'Bar plot y axis label'),
|
|---|
| 843 | arcGISCategory=_(u'Bar plot options'))
|
|---|
| 844 |
|
|---|
| 845 | AddArgumentMetadata(Periodicity.AnalyzeTable, u'scaleFactor',
|
|---|
| 846 | typeMetadata=FloatTypeMetadata(mustBeGreaterThan=0., canBeNone=True),
|
|---|
| 847 | description=_(
|
|---|
| 848 | u"""Scale factor. If provided, the values will be multiplied by this
|
|---|
| 849 | value prior to plotting.
|
|---|
| 850 |
|
|---|
| 851 | Use this value to scale the y axis values to a preferred range. For
|
|---|
| 852 | example, which calculating catch per unit effort (CPUE) for longline
|
|---|
| 853 | fishing gear, it is traditional to present catch per 1000 hooks. But
|
|---|
| 854 | your Value Field contains the amount of catch and your Normalization
|
|---|
| 855 | Field contains the number of hooks, the unscaled result will be catch
|
|---|
| 856 | per 1 hook. To plot catch per 1000 hooks, provide 1000 for the Scale
|
|---|
| 857 | Factor parameter."""),
|
|---|
| 858 | arcGISDisplayName=_(u'Scale factor'),
|
|---|
| 859 | arcGISCategory=_(u'Bar plot options'))
|
|---|
| 860 |
|
|---|
| 861 | AddArgumentMetadata(Periodicity.AnalyzeTable, u'samplingPeriod',
|
|---|
| 862 | typeMetadata=FloatTypeMetadata(mustBeGreaterThan=0., canBeNone=True),
|
|---|
| 863 | description=_(
|
|---|
| 864 | u"""Sampling period, in days, for the Fourier analysis used to produce
|
|---|
| 865 | the periodogram.
|
|---|
| 866 |
|
|---|
| 867 | If you do not supply a value, one will be selected for you. If the
|
|---|
| 868 | dates in your table include time components, 1 hour will be used. If
|
|---|
| 869 | they do not, 1 day will be used.
|
|---|
| 870 |
|
|---|
| 871 | The sampling period determines the period of cycles that can be
|
|---|
| 872 | detected. The shorter the sampling period (i.e. the higher the
|
|---|
| 873 | sampling frequency), the shorter the cycles that can be detected, with
|
|---|
| 874 | the shortest detectable cycle equal to twice the sampling period.
|
|---|
| 875 |
|
|---|
| 876 | If you specify a sampling period of less than 1 day but the dates in
|
|---|
| 877 | your table do not include time components, or all of the time values
|
|---|
| 878 | are the same, a sampling period of 1 day will be used. This is
|
|---|
| 879 | because, when all the times are the same, strong periodicity will be
|
|---|
| 880 | detected the period of 1 day (because no records will occur at
|
|---|
| 881 | fractional days) and the harmonics of 1 day (e.g. 1/2 day, 1/3 day,
|
|---|
| 882 | 1/4 day, and so on). These strong but spurious cycles will produce
|
|---|
| 883 | spikes in the periodogram at those periods, obscuring the cycles of
|
|---|
| 884 | true interest that occur at higher periods."""),
|
|---|
| 885 | arcGISDisplayName=_(u'Sampling period'),
|
|---|
| 886 | arcGISCategory=_(u'Periodogram options'))
|
|---|
| 887 |
|
|---|
| 888 | AddArgumentMetadata(Periodicity.AnalyzeTable, u'maxPeriod',
|
|---|
| 889 | typeMetadata=FloatTypeMetadata(mustBeGreaterThan=0., canBeNone=True),
|
|---|
| 890 | description=_(
|
|---|
| 891 | u"""Maximum period, in days, to plot on the periodogram.
|
|---|
| 892 |
|
|---|
| 893 | By default, this parameter is set to 400 days, under the observation
|
|---|
| 894 | that natural ecological phenomena frequenty exhibit annual periodicity
|
|---|
| 895 | (which results in a spike in the periodogram at 365 days) but that
|
|---|
| 896 | most users of this tool will either not be interested in or have the
|
|---|
| 897 | data to detect cycles having longer periods. If you have sufficient
|
|---|
| 898 | data and wish to check for longer cycles (e.g. decadal oscillations),
|
|---|
| 899 | increase this parameter."""),
|
|---|
| 900 | arcGISDisplayName=_(u'Maximum period to plot'),
|
|---|
| 901 | arcGISCategory=_(u'Periodogram options'))
|
|---|
| 902 |
|
|---|
| 903 | AddArgumentMetadata(Periodicity.AnalyzeTable, u'periodogramMethod',
|
|---|
| 904 | typeMetadata=UnicodeStringTypeMetadata(allowedValues=[u'Lomb-Scargle'], makeLowercase=True),
|
|---|
| 905 | description=_(
|
|---|
| 906 | u"""Method to use for computing the periodogram. Currently only one
|
|---|
| 907 | method is available:
|
|---|
| 908 |
|
|---|
| 909 | * Lomb-Scargle - the Lomb (1976) and Scargle (1976) method is designed
|
|---|
| 910 | to perform spectral analysis on unevenly sampled data, making it
|
|---|
| 911 | more suitable for analysis of ecological data than the most common
|
|---|
| 912 | method for spectral analysis, the Fast Fourier transform (FFT). This
|
|---|
| 913 | tool uses a Python implementation of Press and Rybicki's (1989)
|
|---|
| 914 | algorithm for the Lomb-Scargle method.
|
|---|
| 915 |
|
|---|
| 916 | References
|
|---|
| 917 |
|
|---|
| 918 | Lomb, N (1976). Least-Squares Frequency Analysis of Unequally Spaced
|
|---|
| 919 | Data. Astrophysics and Space Science 39: 447-462.
|
|---|
| 920 |
|
|---|
| 921 | Press, WH and GB Rybicki (1989). Fast algorithm for spectral analysis
|
|---|
| 922 | of unevenly sampled data. Astrophysical Journal, Part 1, vol. 338, p. 277-280.
|
|---|
| 923 |
|
|---|
| 924 | Scargle, J. (1982). Studies in Astronomical Time Series Analysis II.
|
|---|
| 925 | Statistical Aspects of Spectral Analysis of Unevenly Spaced Data.
|
|---|
| 926 | Astrophysical Journal, Part 1, vol. 263, p. 835-853."""),
|
|---|
| 927 | arcGISDisplayName=_(u'Periodogram method'),
|
|---|
| 928 | arcGISCategory=_(u'Periodogram options'))
|
|---|
| 929 |
|
|---|
| 930 | AddArgumentMetadata(Periodicity.AnalyzeTable, u'oversamplingFactor',
|
|---|
| 931 | typeMetadata=FloatTypeMetadata(minValue=1., canBeNone=True),
|
|---|
| 932 | description=_(
|
|---|
| 933 | u"""Oversampling factor for the Lomb-Scargle method.
|
|---|
| 934 |
|
|---|
| 935 | In order to produce accurate values for the peaks in the peridogram,
|
|---|
| 936 | it is necessary to sample the input data at a higher frequency than is
|
|---|
| 937 | necessary to merely detect a short-period cycle. This is called
|
|---|
| 938 | oversampling. In practice, an oversampling factor of 4 or higher is
|
|---|
| 939 | recommended for the Lomb-Scargle method. (The effective sampling
|
|---|
| 940 | period is the Sampling Period parameter divided by the Oversampling
|
|---|
| 941 | Factor parameter.)"""),
|
|---|
| 942 | arcGISDisplayName=_(u'Oversampling factor'),
|
|---|
| 943 | arcGISCategory=_(u'Periodogram options'))
|
|---|
| 944 |
|
|---|
| 945 | AddArgumentMetadata(Periodicity.AnalyzeTable, u'outputFile',
|
|---|
| 946 | typeMetadata=FileTypeMetadata(deleteIfParameterIsTrue=u'overwriteExisting', createParentDirectories=True, canBeNone=True),
|
|---|
| 947 | description=_(
|
|---|
| 948 | u"""Output plot file.
|
|---|
| 949 |
|
|---|
| 950 | If this parameter is not provided (the default), a plot will be
|
|---|
| 951 | created in an interactive window.
|
|---|
| 952 |
|
|---|
| 953 | If this parameter is provided, a plot will be written to the file and
|
|---|
| 954 | an interactive window will not be created. The extension of the file
|
|---|
| 955 | determines its format and may be:
|
|---|
| 956 |
|
|---|
| 957 | * .emf - Enhanced Metafile
|
|---|
| 958 | * .eps - Encapsulated Postscript
|
|---|
| 959 | * .pdf - Portable Document Format
|
|---|
| 960 | * .png - Portable Network Graphics
|
|---|
| 961 | * .ps - Postscript
|
|---|
| 962 | * .raw - Raw RGBA bitmap
|
|---|
| 963 | * .rgba - Raw RGBA bitmap
|
|---|
| 964 | * .svg - Scalable Vector Graphics
|
|---|
| 965 | * .svgz - Scalable Vector Graphics
|
|---|
| 966 | """),
|
|---|
| 967 | direction=u'Output',
|
|---|
| 968 | arcGISDisplayName=_(u'Output plot file'),
|
|---|
| 969 | arcGISCategory=_(u'Other plotting options'))
|
|---|
| 970 |
|
|---|
| 971 | AddArgumentMetadata(Periodicity.AnalyzeTable, u'title',
|
|---|
| 972 | typeMetadata=UnicodeStringTypeMetadata(canBeNone=True),
|
|---|
| 973 | description=_(
|
|---|
| 974 | u"""Title of the plot, to appear at the very top."""),
|
|---|
| 975 | arcGISDisplayName=_(u'Plot title'),
|
|---|
| 976 | arcGISCategory=_(u'Other plotting options'))
|
|---|
| 977 |
|
|---|
| 978 | AddArgumentMetadata(Periodicity.AnalyzeTable, u'width',
|
|---|
| 979 | typeMetadata=FloatTypeMetadata(mustBeGreaterThan=0.),
|
|---|
| 980 | description=_(
|
|---|
| 981 | u"""Width of the output plot file, in inches. This parameter is
|
|---|
| 982 | ignored if the plot is not written to an output file."""),
|
|---|
| 983 | arcGISDisplayName=_(u'Plot width'),
|
|---|
| 984 | arcGISCategory=_(u'Other plotting options'))
|
|---|
| 985 |
|
|---|
| 986 | AddArgumentMetadata(Periodicity.AnalyzeTable, u'dpi',
|
|---|
| 987 | typeMetadata=FloatTypeMetadata(mustBeGreaterThan=0.),
|
|---|
| 988 | description=_(
|
|---|
| 989 | u"""Dots per inch (DPI) of the output plot file. This parameter is
|
|---|
| 990 | ignored if the plot is not written to an output file, and may not
|
|---|
| 991 | apply for vector-based output formats."""),
|
|---|
| 992 | arcGISDisplayName=_(u'Plot DPI'),
|
|---|
| 993 | arcGISCategory=_(u'Other plotting options'))
|
|---|
| 994 |
|
|---|
| 995 | AddArgumentMetadata(Periodicity.AnalyzeTable, u'dayOfYearPlot',
|
|---|
| 996 | typeMetadata=UnicodeStringTypeMetadata(allowedValues=[u'Automatic', u'No', u'Yes'], makeLowercase=True),
|
|---|
| 997 | description=_(
|
|---|
| 998 | u"""Specifies whether or not a radial bar plot should be generated
|
|---|
| 999 | showing the distribution of values according day of the year. If
|
|---|
| 1000 | 'Automatic' is specified, a plot will be generated if the data span
|
|---|
| 1001 | 335 days or more.
|
|---|
| 1002 |
|
|---|
| 1003 | Each bar of the plot encompasses 5 days (the first bin is January 1-5,
|
|---|
| 1004 | the second is January 6-10, and so on). On leap years, the last bar
|
|---|
| 1005 | encompasses 6 days.
|
|---|
| 1006 |
|
|---|
| 1007 | The values of the bars are calculated similar to the main bar plot: if
|
|---|
| 1008 | a Value Field is not specified, each 5-day bar shows the count of
|
|---|
| 1009 | records for those days. If a Value Field is specified but a
|
|---|
| 1010 | Normalization Field is not, each bar shows the sum or mean of the
|
|---|
| 1011 | values for those days (the Aggregation Method parameter specifies
|
|---|
| 1012 | whether the sum or mean is used). If both a Value Field and
|
|---|
| 1013 | Normalization Field are specified, each bar shows the sum of the Value
|
|---|
| 1014 | Field divided by the sum of the Normalization Field for those
|
|---|
| 1015 | days."""),
|
|---|
| 1016 | arcGISDisplayName=_(u'Create day of year plot'),
|
|---|
| 1017 | arcGISCategory=_(u'Other plotting options'))
|
|---|
| 1018 |
|
|---|
| 1019 | AddArgumentMetadata(Periodicity.AnalyzeTable, u'moonPhasePlot',
|
|---|
| 1020 | typeMetadata=UnicodeStringTypeMetadata(allowedValues=[u'Automatic', u'No', u'Yes'], makeLowercase=True),
|
|---|
| 1021 | description=_(
|
|---|
| 1022 | u"""Specifies whether or not a radial bar plot should be generated
|
|---|
| 1023 | showing the distribution of values according to the phase of the moon
|
|---|
| 1024 | calculated from the records' dates. If 'Automatic' is specified, a
|
|---|
| 1025 | plot will be generated if the data span 28 days or more.
|
|---|
| 1026 |
|
|---|
| 1027 | The plot has 30 equal-width bars, each corresponding to about 1 day
|
|---|
| 1028 | (the moon phase is actually about 29.53 days long, so each bar is
|
|---|
| 1029 | about 29.53/30 days long).
|
|---|
| 1030 |
|
|---|
| 1031 | The values of the bars are calculated similar to the main bar plot: if
|
|---|
| 1032 | a Value Field is not specified, each bar shows the count of records
|
|---|
| 1033 | for that fraction of the moon phase. If a Value Field is specified but
|
|---|
| 1034 | a Normalization Field is not, each bar shows the sum or mean of the
|
|---|
| 1035 | values for that fraction (the Aggregation Method parameter specifies
|
|---|
| 1036 | whether the sum or mean is used). If both a Value Field and
|
|---|
| 1037 | Normalization Field are specified, each bar shows the sum of the Value
|
|---|
| 1038 | Field divided by the sum of the Normalization Field for that
|
|---|
| 1039 | fraction."""),
|
|---|
| 1040 | arcGISDisplayName=_(u'Create moon phase plot'),
|
|---|
| 1041 | arcGISCategory=_(u'Other plotting options'))
|
|---|
| 1042 |
|
|---|
| 1043 | AddArgumentMetadata(Periodicity.AnalyzeTable, u'timeOfDayPlot',
|
|---|
| 1044 | typeMetadata=UnicodeStringTypeMetadata(allowedValues=[u'Automatic', u'No', u'Yes'], makeLowercase=True),
|
|---|
| 1045 | description=_(
|
|---|
| 1046 | u"""Specifies whether or not a radial bar plot should be generated
|
|---|
| 1047 | showing the distribution of values according to the time of the day.
|
|---|
| 1048 | If 'Automatic' is specified, a plot will be generated if the data span
|
|---|
| 1049 | 23 hours or more.
|
|---|
| 1050 |
|
|---|
| 1051 | The plot has 24 hour-long bars.
|
|---|
| 1052 |
|
|---|
| 1053 | The values of the bars are calculated similar to the main bar plot: if
|
|---|
| 1054 | a Value Field is not specified, each bar shows the count of records
|
|---|
| 1055 | for that hour of the day. If a Value Field is specified but a
|
|---|
| 1056 | Normalization Field is not, each bar shows the sum or mean of the
|
|---|
| 1057 | values for that hour (the Aggregation Method parameter specifies
|
|---|
| 1058 | whether the sum or mean is used). If both a Value Field and
|
|---|
| 1059 | Normalization Field are specified, each bar shows the sum of the Value
|
|---|
| 1060 | Field divided by the sum of the Normalization Field for that
|
|---|
| 1061 | hour."""),
|
|---|
| 1062 | arcGISDisplayName=_(u'Create time of day plot'),
|
|---|
| 1063 | arcGISCategory=_(u'Other plotting options'))
|
|---|
| 1064 |
|
|---|
| 1065 | AddArgumentMetadata(Periodicity.AnalyzeTable, u'overwriteExisting',
|
|---|
| 1066 | typeMetadata=BooleanTypeMetadata(),
|
|---|
| 1067 | description=_(
|
|---|
| 1068 | u"""If True, the output plot file will be overwritten, if it exists.
|
|---|
| 1069 | If False, a ValueError will be raised if the output plot file
|
|---|
| 1070 | exists."""))
|
|---|
| 1071 |
|
|---|
| 1072 | # Public method: Periodicity.AnalyzeArcGISTable
|
|---|
| 1073 |
|
|---|
| 1074 | AddMethodMetadata(Periodicity.AnalyzeArcGISTable,
|
|---|
| 1075 | shortDescription=_(u'Creates several plots that show the temporal periodicity of the records in a table.'),
|
|---|
| 1076 | longDescription=Periodicity.AnalyzeTable.__doc__.Obj.LongDescription,
|
|---|
| 1077 | isExposedToPythonCallers=True,
|
|---|
| 1078 | isExposedByCOM=True,
|
|---|
| 1079 | isExposedAsArcGISTool=True,
|
|---|
| 1080 | dependencies=[ArcGISDependency(9, 1), MatplotlibDependency()])
|
|---|
| 1081 |
|
|---|
| 1082 | CopyArgumentMetadata(Periodicity.AnalyzeTable, u'cls', Periodicity.AnalyzeArcGISTable, u'cls')
|
|---|
| 1083 |
|
|---|
| 1084 | AddArgumentMetadata(Periodicity.AnalyzeArcGISTable, u'table',
|
|---|
| 1085 | typeMetadata=ArcGISTableViewTypeMetadata(mustExist=True),
|
|---|
| 1086 | description=Periodicity.AnalyzeTable.__doc__.Obj.Arguments[1].Description,
|
|---|
| 1087 | arcGISDisplayName=_(u'Table'))
|
|---|
| 1088 |
|
|---|
| 1089 | AddArgumentMetadata(Periodicity.AnalyzeArcGISTable, u'dateField',
|
|---|
| 1090 | typeMetadata=ArcGISFieldTypeMetadata(mustExist=True, allowedFieldTypes=[u'date']),
|
|---|
| 1091 | description=Periodicity.AnalyzeTable.__doc__.Obj.Arguments[2].Description,
|
|---|
| 1092 | arcGISDisplayName=_(u'Date field'),
|
|---|
| 1093 | arcGISParameterDependencies=[u'table'])
|
|---|
| 1094 |
|
|---|
| 1095 | AddArgumentMetadata(Periodicity.AnalyzeArcGISTable, u'valueField',
|
|---|
| 1096 | typeMetadata=ArcGISFieldTypeMetadata(mustExist=True, allowedFieldTypes=[u'SHORT', u'LONG', u'FLOAT', u'DOUBLE'], canBeNone=True),
|
|---|
| 1097 | description=Periodicity.AnalyzeTable.__doc__.Obj.Arguments[3].Description,
|
|---|
| 1098 | arcGISDisplayName=_(u'Value field'),
|
|---|
| 1099 | arcGISParameterDependencies=[u'table'])
|
|---|
| 1100 |
|
|---|
| 1101 | AddArgumentMetadata(Periodicity.AnalyzeArcGISTable, u'normalizationField',
|
|---|
| 1102 | typeMetadata=ArcGISFieldTypeMetadata(mustExist=True, allowedFieldTypes=[u'SHORT', u'LONG', u'FLOAT', u'DOUBLE'], canBeNone=True),
|
|---|
| 1103 | description=Periodicity.AnalyzeTable.__doc__.Obj.Arguments[4].Description,
|
|---|
| 1104 | arcGISDisplayName=_(u'Normalization field'),
|
|---|
| 1105 | arcGISParameterDependencies=[u'table'])
|
|---|
| 1106 |
|
|---|
| 1107 | CopyArgumentMetadata(Periodicity.AnalyzeTable, u'aggregationMethod', Periodicity.AnalyzeArcGISTable, u'aggregationMethod')
|
|---|
| 1108 |
|
|---|
| 1109 | AddArgumentMetadata(Periodicity.AnalyzeArcGISTable, u'where',
|
|---|
| 1110 | typeMetadata=SQLWhereClauseTypeMetadata(canBeNone=True),
|
|---|
| 1111 | description=_(
|
|---|
| 1112 | u"""SQL WHERE clause expression that specifies the subset of rows to
|
|---|
| 1113 | use. If this parameter is not provided, all of the rows will be used.
|
|---|
| 1114 |
|
|---|
| 1115 | The exact syntax of this expression depends on the type of feature
|
|---|
| 1116 | class you're using. ESRI recommends you reference fields using the
|
|---|
| 1117 | following syntax:
|
|---|
| 1118 |
|
|---|
| 1119 | * For shapefiles, ArcInfo coverages, or feature classes stored in file
|
|---|
| 1120 | geodatabases, ArcSDE geodatabases, or ArcIMS, enclose field names in
|
|---|
| 1121 | double quotes: "MY_FIELD"
|
|---|
| 1122 |
|
|---|
| 1123 | * For feature classes stored in personal geodatabases, enclose field
|
|---|
| 1124 | names in square brackets: [MY_FIELD].
|
|---|
| 1125 | """),
|
|---|
| 1126 | arcGISDisplayName=_(u'Where clause'),
|
|---|
| 1127 | arcGISParameterDependencies=[u'table'])
|
|---|
| 1128 |
|
|---|
| 1129 | CopyArgumentMetadata(Periodicity.AnalyzeTable, u'binWidth', Periodicity.AnalyzeArcGISTable, u'binWidth')
|
|---|
| 1130 | CopyArgumentMetadata(Periodicity.AnalyzeTable, u'timeUnits', Periodicity.AnalyzeArcGISTable, u'timeUnits')
|
|---|
| 1131 | CopyArgumentMetadata(Periodicity.AnalyzeTable, u'yAxisLabel', Periodicity.AnalyzeArcGISTable, u'yAxisLabel')
|
|---|
| 1132 | CopyArgumentMetadata(Periodicity.AnalyzeTable, u'scaleFactor', Periodicity.AnalyzeArcGISTable, u'scaleFactor')
|
|---|
| 1133 | CopyArgumentMetadata(Periodicity.AnalyzeTable, u'samplingPeriod', Periodicity.AnalyzeArcGISTable, u'samplingPeriod')
|
|---|
| 1134 | CopyArgumentMetadata(Periodicity.AnalyzeTable, u'maxPeriod', Periodicity.AnalyzeArcGISTable, u'maxPeriod')
|
|---|
| 1135 | CopyArgumentMetadata(Periodicity.AnalyzeTable, u'periodogramMethod', Periodicity.AnalyzeArcGISTable, u'periodogramMethod')
|
|---|
| 1136 | CopyArgumentMetadata(Periodicity.AnalyzeTable, u'oversamplingFactor', Periodicity.AnalyzeArcGISTable, u'oversamplingFactor')
|
|---|
| 1137 | CopyArgumentMetadata(Periodicity.AnalyzeTable, u'outputFile', Periodicity.AnalyzeArcGISTable, u'outputFile')
|
|---|
| 1138 | CopyArgumentMetadata(Periodicity.AnalyzeTable, u'title', Periodicity.AnalyzeArcGISTable, u'title')
|
|---|
| 1139 | CopyArgumentMetadata(Periodicity.AnalyzeTable, u'width', Periodicity.AnalyzeArcGISTable, u'width')
|
|---|
| 1140 | CopyArgumentMetadata(Periodicity.AnalyzeTable, u'dpi', Periodicity.AnalyzeArcGISTable, u'dpi')
|
|---|
| 1141 | CopyArgumentMetadata(Periodicity.AnalyzeTable, u'dayOfYearPlot', Periodicity.AnalyzeArcGISTable, u'dayOfYearPlot')
|
|---|
| 1142 | CopyArgumentMetadata(Periodicity.AnalyzeTable, u'moonPhasePlot', Periodicity.AnalyzeArcGISTable, u'moonPhasePlot')
|
|---|
| 1143 | CopyArgumentMetadata(Periodicity.AnalyzeTable, u'timeOfDayPlot', Periodicity.AnalyzeArcGISTable, u'timeOfDayPlot')
|
|---|
| 1144 |
|
|---|
| 1145 | AddArgumentMetadata(Periodicity.AnalyzeArcGISTable, u'overwriteExisting',
|
|---|
| 1146 | typeMetadata=BooleanTypeMetadata(),
|
|---|
| 1147 | description=_(
|
|---|
| 1148 | u"""If True, the output plot file will be overwritten, if it exists.
|
|---|
| 1149 | If False, a ValueError will be raised if the output plot file
|
|---|
| 1150 | exists."""),
|
|---|
| 1151 | initializeToArcGISGeoprocessorVariable=u'OverwriteOutput')
|
|---|
| 1152 |
|
|---|
| 1153 | ###############################################################################
|
|---|
| 1154 | # Names exported by this module
|
|---|
| 1155 | ###############################################################################
|
|---|
| 1156 |
|
|---|
| 1157 | __all__ = ['Periodicity']
|
|---|