Ejemplo n.º 1
0
def makeFit(inValByYear, maxSize, verbose=False):
    retValByYear = {}
    #Python2: for (year, value) in inValByYear.iteritems():
    for (year, value) in inValByYear.items():
        if value != None:
            retValByYear[year] = value

    retYearByVal = reverseDict(retValByYear)

    # Remove the oldest year that is not the max or min value.
    while len(retValByYear) > maxSize:
        years = sorted(retValByYear.keys())
        firstBoringYearIndex = 0
        while (firstBoringYearIndex < len(years)
               and ((retValByYear[years[firstBoringYearIndex]] in (min(
                   retValByYear.values()), max(retValByYear.values()))) or
                    (distanceFromThisYear(years[firstBoringYearIndex],
                                          retValByYear, retYearByVal) < 5))):
            firstBoringYearIndex += 1

        firstBoringYear = 9999
        if firstBoringYearIndex < len(years):
            firstBoringYear = years[firstBoringYearIndex]
        if firstBoringYear > max(retValByYear.keys()) - 30:
            if verbose:
                print("Ran out of years to throw out.")
            return retValByYear

        if verbose:
            print("Throwing out %d so the rest will fit" % firstBoringYear)
        del retValByYear[firstBoringYear]
    return retValByYear
Ejemplo n.º 2
0
def maybeTweetTop5(city, valByYear, nextMonthYear, nextMonthMonth, monthStr,
                   field, monthDate):
    yearByVal = reverseDict(valByYear)
    top5 = topNValuesLists(yearByVal, 5)
    if inListOfLists(top5.values(), monthDate.year):
        daysLeft = datetime.date(nextMonthYear, nextMonthMonth,
                                 1) - datetime.date.today()
        tweetTopN(city, top5, monthStr, field, monthDate.year, daysLeft.days)
Ejemplo n.º 3
0
def annualOrderedByMonth(city, monthFilter, field, cumulative):
    valByYears = yearDatas(monthFilter, field, now.year, cumulative)
    plotDataOtherYears = []
    plotDataThisYear = []
    plotTics = []
    plotIndex = 0

    divider = 1
    ylabel = field.units
    if max(valByYears.values()) > 10000:
        divider = 1000.0
        ylabel = '1000s of %s' % field.units

    valByYears = makeFit(valByYears, 75)
    plotIndex = 0
    last30Years = []

    yearsByVal = reverseDict(valByYears)
    print(yearsByVal)

    for val in sorted(yearsByVal.keys()):
        yearsWithThisVal = yearsByVal[val]
        for plotYear in yearsWithThisVal:
            if now.year == plotYear:
                plotDataThisYear.append((plotIndex, val / divider))
            else:
                plotDataOtherYears.append((plotIndex, val / divider))
            plotTics.append('"%s" %d' % (plotYear, plotIndex))
            plotIndex += 1
            if (plotYear > now.year - 31) and (plotYear < now.year):
                last30Years.append(val / divider)

    avg = sum(last30Years) / len(last30Years)
    std = numpy.std(last30Years)
    #print
    if len(yearsByVal.keys()) > 0:
        plot = gnuplot.Plot('%s/svg/yearOrdering.%s.%s' %
                            (city, monthFilter.filenamePart,
                             field.title.replace(' ', '_').replace('℃', 'C')),
                            yaxis=2)
        legend = 'on left'
        if max(yearsByVal.keys()) < 0:
            legend = 'bottom right'
        ymin = None
        if cumulative:
            ymin = 0
        plot.open(
            xtics=plotTics,
            xticsRotate=90,
            xticsFont='Arial,10',
            legend=legend,
            ymin=ymin,
            title='%s %s for %s' %
            (city.capitalize(), field.title.lower(), monthFilter.chartTitle),
            margins=[6, 8, 2, 3],
            ylabel=ylabel)
        plot.addLine(
            gnuplot.Line('30-year std-dev',
                         ((-1, avg - std), (plotIndex, avg - std),
                          (plotIndex, avg + std), (-1, avg + std)),
                         lineColour='#e3e3e3',
                         plot='filledcurves'))
        plot.addLine(
            gnuplot.Line('Other years', plotDataOtherYears, plot='boxes'))
        plot.addLine(gnuplot.Line('This year', plotDataThisYear, plot='boxes'))
        plot.addLine(
            gnuplot.Line('30-Year Average', ((0, avg), (plotIndex - 1, avg))))
        plot.plot()
        plot.close()
Ejemplo n.º 4
0
def plotdict(valueByYear,
             filename,
             chartTitle,
             yaxisLabel,
             thisYear,
             plotZeros=True,
             output='svg',
             ymin=None,
             showAverage=True):
    yearByValue = reverseDict(valueByYear)
    values = sorted(yearByValue.keys())
    #
    chartTicks = []
    chartDataOtherYears = []
    chartDataThisYear = []
    chartIndex = 0
    maxValue = max(values)

    if showAverage:
        recentYears = tuple(
            filter(lambda t: t in valueByYear, range(thisYear - 30, thisYear)))
        recentValues = [valueByYear[t] for t in recentYears]
        recentAvg = sum(recentValues) / len(recentValues)
        recentStd = numpy.std(tuple(map(float, recentValues)))

    longestXTick = 0
    for value in values:
        # number of years with this value
        #print 'val="%s"' % value
        thiscount = len(yearByValue[value])
        #
        if plotZeros == False and value == 0:
            # We've been told to skip zeros, so we don't plot them
            continue

        for year in yearByValue[value]:
            if year == thisYear:
                chartDataThisYear.append((chartIndex, value, '# ' + str(year)))
            else:
                chartDataOtherYears.append(
                    (chartIndex, value, '# ' + str(year)))
            xtick = str(year)
            longestXTick = max(longestXTick, len(xtick))
            chartTicks.append('"{date}" {index}'.format(date=xtick,
                                                        index=chartIndex))
            chartIndex += 1

    legend = 'on left'
    if maxValue < 0:
        legend = 'on right bottom'
    bmargin = 2 + longestXTick // 2

    plot = gnuplot.Plot(filename, yaxis=2, output=output)
    #
    #, xticsFont='Arial,10'
    plot.open(title=chartTitle,
              xtics=chartTicks,
              xticsRotate=90,
              xticsFont='Arial,20',
              legend=legend,
              margins=[6, 8, 2, bmargin],
              ylabel=yaxisLabel,
              ymin=ymin,
              xmin=-1,
              xmax=chartIndex)
    if showAverage:
        plot.addLine(
            gnuplot.Line('30-year 2*std-dev',
                         ((-1, recentAvg - recentStd * 2),
                          (chartIndex, recentAvg - recentStd * 2),
                          (chartIndex, recentAvg + recentStd * 2),
                          (-1, recentAvg + recentStd * 2)),
                         lineColour='#f1f1f1',
                         plot='filledcurves'))
        plot.addLine(
            gnuplot.Line('30-year std-dev',
                         ((-1, recentAvg - recentStd),
                          (chartIndex, recentAvg - recentStd),
                          (chartIndex, recentAvg + recentStd),
                          (-1, recentAvg + recentStd)),
                         lineColour='#e3e3e3',
                         plot='filledcurves'))
        plot.addLine(
            gnuplot.Line('30-year average',
                         ((-1, recentAvg), (chartIndex, recentAvg)),
                         lineColour='0x000000'))
    plot.addLine(
        gnuplot.Line('Other years',
                     chartDataOtherYears,
                     plot='boxes',
                     lineColour='0x6495ED'))
    plot.addLine(
        gnuplot.Line('This year',
                     chartDataThisYear,
                     plot='boxes',
                     lineColour='0x556B2F'))
    plot.plot()
    plot.close()
    return plot.fname
Ejemplo n.º 5
0
def first(cityName,
          expr,
          name,
          dateRange,
          excludeThisYear=False,
          order="first",
          yaxisLabel=None,
          run=1,
          limitToMostRecentYears=None,
          verboseIfInDateRange=None,
):
    data = daily.load(cityName)
    if limitToMostRecentYears != None:
        ndata = daily.Data()
        yearsAgo = datetime.date(now.year-limitToMostRecentYears, now.month, now.day)
        for key in data:
            if key >= yearsAgo:
                ndata[key] = data[key]
        data = ndata
    fieldDict = {
        'min': Value(daily.MIN_TEMP),
        'max': Value(daily.MAX_TEMP),
        'tempSpan': ValueDiff(daily.MAX_TEMP, daily.MIN_TEMP),
        'rain': Value(daily.TOTAL_RAIN_MM),
        'humidex': Value(daily.MAX_HUMIDEX),
        'snow': Value(daily.TOTAL_SNOW_CM),
        'snowpack': ValueEmptyZero(daily.SNOW_ON_GRND_CM),
        'windgust': Value(daily.SPD_OF_MAX_GUST_KPH),
        'wind': Value(daily.AVG_WIND),
        'windchill': Value(daily.MIN_WINDCHILL),
        'avgWindchill': Value(daily.MIN_WINDCHILL),
    }
    fieldValues = fieldDict.values()
    referencedValues=[]
    for fieldName in fieldDict.keys():
        if '{'+fieldName+'}' in expr:
            referencedValues.append(fieldName)

    firstByYear = {}
    eThisYear = now.year
    endTimeThisYear = datetime.date(now.year, dateRange.endMonth, dateRange.endDay)
    if dateRange.yearCross() and now < endTimeThisYear:
        eThisYear -= 1


    for baseyear in range(data.minYear, data.maxYear-dateRange.todayIsYearCross()+1):

        try:
            dayOffset = datacache.readCache(cityName, baseyear,
                                            '{}.{}.{}.{}'.format(name,order,str(dateRange),expr))
            if dayOffset != None:
                firstByYear[baseyear] = dayOffset
            continue
        except datacache.NotInCache:
            pass
        starttime = datetime.date(baseyear, dateRange.startMonth, dateRange.startDay)
        endtime = datetime.date(baseyear+dateRange.yearCross(), dateRange.endMonth, dateRange.endDay)

        #print baseyear, starttime, endtime
        dayRange = daily.dayRange(starttime,endtime)
        if order=="last":
            dayRange = daily.dayRange(endtime-datetime.timedelta(1), starttime-datetime.timedelta(1), -1)
            if excludeThisYear and baseyear == now.year-dateRange.yearCross():
                # don't consider this year when looking for records for last event, because this year is not over
                continue

        expectedDayCount = 0
        observedDayCount = 0
        for day in dayRange:
            expectedDayCount += 1
            if day in data:
                observedDayCount += 1
            else:
                continue

            if baseyear in firstByYear:
                # We already figured out which day was first
                continue

            vals = {}
            for fieldName, fieldCall in fieldDict.items():
                try:
                    vals[fieldName] = fieldCall(data[day], day)
                    vals[fieldName+'Flag'] = '"' + fieldCall.getFlag(data[day]) + '"'
                except KeyError:
                    #print day, 'KeyError'
                    vals[fieldName] = None
            skip=False
            usedVals={}
            for fieldName in fieldDict.keys():
                if fieldName in referencedValues:
                    if vals[fieldName] is None:
                        #print 'Skipping {} because {} is None'.format(day, fieldName)
                        skip=True
                        break
                    usedVals[fieldName] = vals[fieldName]
            if skip:
                continue

            #resolvedExpr = expr.format(**vals)
            #print(vals)
            #val = eval(expr)
            val = eval(expr, vals)

            #if True: #day == datetime.date(2015,10,17):
            #print day, resolvedExpr, val

            if val is True:
                dayOffset = (day - starttime).days
                firstByYear[baseyear] = dayOffset
                break

        observedPercent = observedDayCount * 100 / expectedDayCount
        if observedPercent < 85 and baseyear != eThisYear:
            print('Skipping {baseyear} because it only had {observedPercent:.1f}% of the days'.format(**locals()))
            if baseyear in firstByYear:
                firstByYear.pop(baseyear)
        elif baseyear not in firstByYear and baseyear != eThisYear:
            print('Event did not happen during {}.'.format(baseyear))
        datacache.cacheThis(cityName, baseyear,
                            '{}.{}.{}.{}'.format(name,order,str(dateRange),expr),
                            firstByYear.get(baseyear, None))


    yearByFirst = reverseDict(firstByYear)
    #for offset in sorted(yearByFirst.keys()):
    #    for year in yearByFirst[offset]:
    #        print datetime.date(year, dateRange.startMonth, dateRange.startDay) + datetime.timedelta(offset)
    #print yearByFirst

    verbose = False
    if verboseIfInDateRange == None:
        verbose = True
    elif eThisYear in firstByYear:
        thisYearFirstDayOffset = firstByYear[eThisYear]
        firstThisYearDate = (
            datetime.date(eThisYear, dateRange.startMonth, dateRange.startDay)
            + datetime.timedelta(thisYearFirstDayOffset) )
        if ( firstThisYearDate >= verboseIfInDateRange[0]
             and firstThisYearDate <= verboseIfInDateRange[1] ):
            verbose = True

    (earliest, secondEarliest, *ignore) = sorted(filter(lambda y: y!=now.year-dateRange.yearCross(), yearByFirst.keys()))[:2] + [None,None]
    (secondLatest, latest, *ignore) = sorted(filter(lambda y: y!=now.year-dateRange.yearCross(), yearByFirst.keys()))[-2:] + [None,None]

    earliestYears = lookupEmptyListIfNone(yearByFirst, earliest)
    secondEarliestYears = lookupEmptyListIfNone(yearByFirst, secondEarliest)

    latestYears = lookupEmptyListIfNone(yearByFirst, latest)
    secondLatestYears = lookupEmptyListIfNone(yearByFirst, secondLatest)

    earliestDates=[]
    latestDates=[]
    if verbose:
        earliestDates=showRecords(name, 'earliest', earliestYears, firstByYear, dateRange)
        showRecords(name, '2nd earliest', secondEarliestYears, firstByYear, dateRange)
        showRecords(name, '2nd latest', secondLatestYears, firstByYear, dateRange)
        latestDates=showRecords(name, 'latest', latestYears, firstByYear, dateRange)

    avgKeys = filter(lambda t: t in firstByYear, range(eThisYear-30, eThisYear))

    offsets = sorted([firstByYear[a] for a in avgKeys])
    avg = calcAvg(offsets)
    median = calcMedian(offsets)

    avgFirst = calcDate(eThisYear, dateRange.startMonth, dateRange.startDay, avg)
    medianFirst = calcDate(eThisYear, dateRange.startMonth, dateRange.startDay, median)
    if verbose:
        print("average {name} is {avgFirst}".format(**locals()))
        print("median {name} is {medianFirst}".format(**locals()))

    ret = None
    if eThisYear in firstByYear:
        thisYearFirst = firstByYear[eThisYear]
        countEarlier = 0
        countEqual = 0
        countLater = 0
        recentCountEarlier = []
        recentCountEqual = []
        recentCountLater = []

        for first in sorted(yearByFirst.keys()):
            if first < thisYearFirst:
                countEarlier += len(yearByFirst[first])
                recentCountEarlier += filter(lambda y: y<eThisYear and y > eThisYear-31, yearByFirst[first])
            elif first == thisYearFirst:
                countEqual += len(yearByFirst[first]) - 1 #Subtract the current year
                recentCountEqual += filter(lambda y: y<eThisYear and y > eThisYear-31, yearByFirst[first])
            else:
                countLater += len(yearByFirst[first])
                recentCountLater += filter(lambda y: y<eThisYear and y > eThisYear-31, yearByFirst[first])

        totalCount = countEarlier + countEqual + countLater
        totalRecentCount = len(recentCountEarlier) + len(recentCountEqual) + len(recentCountLater)

        firstThisYear = ( datetime.date(eThisYear, dateRange.startMonth, dateRange.startDay)
                          + datetime.timedelta(thisYearFirst) )
        if verbose:
            print('%s %s was %s' % (now.year, name, firstThisYear ))
            print('%d%% of last %d years were earlier.'
                  % (round(countEarlier * 100.0 / totalCount), totalCount))
            print('%d%% of last %d years were the same.'
                  % (round(countEqual * 100.0 / totalCount), totalCount))
            print('%d%% of last %d years were later.'
                  % (round(countLater * 100.0 / totalCount), totalCount))
            print('%d of last %d years were earlier.'
                  % (len(recentCountEarlier), totalRecentCount), sorted(recentCountEarlier))
            print('%d of last %d years were the same.'
                  % (len(recentCountEqual), totalRecentCount), sorted(recentCountEqual))
            print('%d of last %d years were later.'
                  % (len(recentCountLater), totalRecentCount), sorted(recentCountLater))
        ret = firstThisYear, medianFirst, earliestDates, latestDates
    else:
        print('Not showing this year because eThisYear="{}" and firstByYear="{}"'.format(eThisYear, firstByYear))

    if len(yearByFirst) == 0:
        # There's nothing to plot, so don't even bother trying
        return ret
    plotDataOtherYears = []
    plotDataThisYear = []
    dateLabels = []
    for key in sorted(yearByFirst.keys()):
        if eThisYear in yearByFirst[key]:
            plotDataThisYear.append((key, len(yearByFirst[key]), '#%s' % ','.join(map(str, yearByFirst[key])) ))
        else:
            plotDataOtherYears.append((key, len(yearByFirst[key]), '#%s' % ','.join(map(str, yearByFirst[key]))))
    histogramFname = '%s/svg/%s' % (cityName, name.replace(' ', '_'))
    if ret != None:
        ret = ret + (histogramFname,)
    plot = gnuplot.Plot(histogramFname)
    dateMin = min(yearByFirst.keys())
    dateMax = max(yearByFirst.keys())
    plotDateMin = dateMin-1 #int(dateMin - (dateMax - dateMin)*.25)
    plotDateMax = dateMax+1 #int(dateMax + (dateMax - dateMin)*.25)

    for dayOffset in range(plotDateMin, plotDateMax+1, 1):
        date = datetime.date(now.year, dateRange.startMonth, dateRange.startDay) + datetime.timedelta(dayOffset)
        if date.day % 5 == 1 and date.day != 31:
            dateLabels.append('"%s" %d' % (date.strftime('%b/%d'), dayOffset))

    ylabel = 'Number of years when %s happened on this day' % name
    if yaxisLabel != None:
        ylabel = yaxisLabel
    plot.open(title='%s in %s' % (name, cityName.capitalize()),
              ylabel=ylabel,
              xmin=plotDateMin,
              xmax=plotDateMax,
              ymin=0,
              ymax=max(len(a) for a in yearByFirst.values())+1,
              xtics=dateLabels, xticsRotate=45,
              margins=[7,8,2,5])
    plot.addLine(gnuplot.Line('Other years', plotDataOtherYears, plot='boxes'))
    plot.addLine(gnuplot.Line('This year', plotDataThisYear, plot='boxes'))
    plot.plot()
    plot.close()

    print('')

    plotdata = []
    for (year, plotCount) in firstByYear.items():
        if plotCount != None:
            plotdata.append(
                (year, plotCount,
                 ( '#'
                   +str(datetime.date(year, dateRange.startMonth, dateRange.startDay)
                        + datetime.timedelta(plotCount)))))

    ytics = []
    for m in range(1,13):
        for d in (1,10,20):
            thisDate = datetime.date(2015,m,d)
            dayOffset = (thisDate - datetime.date(2015, dateRange.startMonth, dateRange.startDay)).days
            ytics.append('"%s" %d' % (thisDate.strftime('%b %d'), dayOffset))

    #print(tuple(t[:2] for t in plotdata))
    lineFit = linear.linearTrend(tuple(t[:2] for t in plotdata))

    plot = gnuplot.Plot("%s/svg/%s_%s-%u_%s.line"
                        % (cityName, name.replace(' ', '_'),
                           monthName(dateRange.startMonth), dateRange.startDay,
                           dateRange.lastDay().replace(' ', '-') ),
                        yaxis=2)
    plot.open(title='%s %s between %s %u and %s'
              % (cityName.capitalize(), name,
                 monthName(dateRange.startMonth), dateRange.startDay,
                 dateRange.lastDay() ),
              ytics=ytics)
    plot.addLine(gnuplot.Line("Linear", lineFit, lineColour='green', plot='lines'))
    plot.addLine(gnuplot.Line("Date", plotdata, lineColour='purple'))
    plot.plot()
    plot.close()
    return ret
Ejemplo n.º 6
0
def main(
    city,
    force=False,
    lastCheckedValue=None,
    today=None,
    maxValueToCheck=None,
    allYear=False,
    allWinter=False,
    justTop5=False,
    dataSinceDay=None,
):
    #import pdb; pdb.set_trace()
    data = daily.load(city)
    if dataSinceDay is not None:
        for day in tuple(data.keys()):
            if day < dataSinceDay:
                del data[day]
    monthlyAverages.cityData = data
    if today is None:
        today = daily.timeByCity[city].date()
        #if allYear:
        #    today = dt.date(daily.timeByCity[city].date().year, 12, 31)
    yearToCheck = (today - dt.timedelta(7)).year
    if yearToCheck != today.year:
        today = dt.date(yearToCheck, 12, 31)
    monthlyAverages.now = today
    tomorrow = today + dt.timedelta(days=1)

    todayMaxInfo = dailyRecords.getInfo(city, today, daily.MAX_TEMP)

    todayAverageMax = roundAwayFromZero(todayMaxInfo.normal)
    todayMax = None
    if todayMaxInfo.recent is not None:
        todayMax = int(todayMaxInfo.recent)

    if lastCheckedValue is None:
        minValueToCheck = int(todayAverageMax) + 2
    else:
        minValueToCheck = int(lastCheckedValue) + 1

    if maxValueToCheck is None:
        maxValueToCheck = todayMax
        if todayMax is None:
            maxValueToCheck = 35

    maxValuesToCheck = filter(lambda t: t % 10 == 0,
                              range(minValueToCheck, maxValueToCheck + 1))

    fieldList = [
        #*[ ( ExprVal('max>={} if max is not None else None'.format(t),
        #             title=str(t) + "℃",
        #             name="max>=" + str(t),
        #             units="days",
        #             unit="day",
        #             precision=0,
        #             field=daily.MAX_TEMP,
        #             description=str(t)+"℃ days"),
        #     True ) for t in maxValuesToCheck ],
        #[ ( ExprVal('maxHumidex>max and maxHumidex>=' + str(t),
        #              title=str(t) + " humidex",
        #              name="humidex>=" + str(t),
        #              units="days",
        #              unit="day",
        #              precision=0),
        #      True ) for t in range(30, 51) ]
        #( FractionVal(daily.TOTAL_RAIN_MM, "Rain"), True ),
        (FractionVal(daily.TOTAL_SNOW_CM, "snow"), True),
        #( FractionVal(daily.TOTAL_PRECIP_MM, "precipitation"), True ),
        #( FractionVal(daily.AVG_WIND, "Wind"), False ),
        #( Avg(daily.MIN_TEMP, daily.MAX_TEMP, "temperature"), False ),
        #( FractionVal(daily.MEAN_HUMIDITY, "Humidity"), False ),
    ]

    monFilter = monthlyAverages.BeforeDateFilter(month=tomorrow.month,
                                                 day=tomorrow.day)
    if allYear:
        monFilter = monthlyAverages.BeforeDateFilter(month=1, day=1)
    if allWinter:
        monFilter = WinterFilter()
    todayFilter = monthlyAverages.OneDayFilter(month=today.month,
                                               day=today.day)

    #import pudb; pu.db
    for field, cumulative in fieldList:
        thisyear = today.year
        if allWinter:
            thisyear = today.year if today.month >= 7 else today.year - 1
        valByYear = monthlyAverages.yearDatas(monFilter,
                                              field=field,
                                              lastYear=thisyear,
                                              cumulative=cumulative)
        normalVal = monthlyAverages.normalMonthlyDatas(today.year,
                                                       monFilter,
                                                       field,
                                                       cumulative=cumulative)
        if thisyear not in valByYear:
            continue
        ci80 = confidenceInterval80(valByYear, thisyear)
        ci50 = confidenceInterval50(valByYear, thisyear)

        thisYearVal = valByYear[thisyear]
        maxSince = None
        minSince = None
        for year in reversed(sorted(valByYear.keys())):
            if year != thisyear:
                val = valByYear[year]
                if maxSince is None and val >= thisYearVal:
                    maxSince = year
                if minSince is None and val <= thisYearVal:
                    minSince = year
        del val
        insideCi80 = (thisYearVal >= ci80.low and thisYearVal <= ci80.high)
        insideCi50 = (thisYearVal >= ci50.low and thisYearVal <= ci50.high)

        if field.units == 'days':  # and False:
            todayValByYear = monthlyAverages.yearDatas(todayFilter,
                                                       field=field,
                                                       lastYear=today.year,
                                                       cumulative=cumulative)
            if todayValByYear[today.year] == 0:
                #The countable event did not occur today, so skip.
                continue
            #import pdb; pdb.set_trace()

        yearByVal = reverseDict(valByYear)
        top5 = topNValuesLists(yearByVal, 5)
        if ((allYear or allWinter)
                and (inListOfLists(top5.values(), thisyear) or args.force)):
            tweetTopN(city, top5, field, thisyear)
        if justTop5:
            continue

        amountDescription = ''
        if thisYearVal < ci50[0] and (field.units != 'days'
                                      or thisYearVal != 0):
            amountDescription = 'just '
        amount = formatWithUnits(thisYearVal, field, field.precision)
        if field.units == 'days':
            amount = formatWithUnits(thisYearVal, field, 0, skipUnits=True)
            if thisYearVal == 0:
                amount = "no"

        aboveBelow = 'above'
        if thisYearVal < normalVal.average:
            aboveBelow = 'below'

        if cumulative:
            deviation = deviationDescription(thisYearVal, normalVal.average,
                                             field)
        else:
            diff = abs(thisYearVal - normalVal.average)
            deviation = formatWithUnits(diff, field,
                                        field.precision) + ' ' + aboveBelow
        start = 'average'
        if cumulative:
            start = 'total'
        title = field.title.lower()
        ci80Low = formatWithUnits(ci80[0],
                                  field,
                                  field.precision,
                                  skipUnits=True)
        ci80High = formatWithUnits(ci80[1], field, field.precision)
        insideOutside = 'outside'
        if insideCi80:
            insideOutside = 'inside'
        cityName = stations.city[city].name
        avgWithUnits = formatWithUnits(normalVal.average, field,
                                       field.precision)
        #tweetText = (
        #    '#{cityName}\'s {start} {title} so far this year was'
        #    ' {amountDescription}{amount},'
        #    ' {deviation} {aboveBelow} average; {insideOutside} the normal range of'
        #    ' {ci80Low} to {ci80High}'.format(**locals()))
        tweetText = ('#{cityName}\'s {start} {title} so far this year was'
                     ' {amountDescription}{amount},'
                     ' {deviation} the average of'
                     ' {avgWithUnits}'.format(**locals()))
        if allYear:
            tweetText = (
                '#{cityName}\'s {start} {title} during {today.year} was'
                ' {amountDescription}{amount},'
                ' {deviation} the average of'
                ' {avgWithUnits}'.format(**locals()))
        if field.units == 'days':
            units = field.unit
            if thisYearVal != 1:
                units = field.units
            nth = alert.nth(thisYearVal)
            #import pdb; pdb.set_trace()
            average = formatWithUnits(normalVal.average, field, precision=1)
            todayString = 'Today is'
            if today != daily.timeByCity[city].date():
                todayString = 'Yesterday was'
            tweetText = (
                '{todayString} #{cityName}\'s {nth} {title} day so far this year,'
                ' {deviation} the average of {average}.'.format(**locals()))
        #alertTweets.maybeTweetWithSuffix(city, tweetText)
        recordMin = normalVal.minimum
        recordMax = normalVal.maximum
        print(
            'Record minimum was {recordMin}{field.units} in {normalVal.minYear}'
            .format(**locals()))
        print(
            'Record maximum was {recordMax}{field.units} in {normalVal.maxYear}'
            .format(**locals()))
        plotIt = not insideCi80 or args.force
        if maxSince is None:
            print('***Max since records began in {}.'.format(
                min(valByYear.keys())))
            plotIt = True
        elif today.year - maxSince > 10:
            maxSinceVal = valByYear[maxSince]
            print('***Max since {maxSince}:{maxSinceVal}{field.units}'.format(
                **locals()))
            plotIt = True
        if minSince is None:
            print('***Min since records began in {}.'.format(
                min(valByYear.keys())))
            plotIt = True
        elif today.year - minSince > 10:
            minSinceVal = valByYear[minSince]
            print('***Min since {minSince}:{minSinceVal}{field.units}'.format(
                **locals()))
            plotIt = True
        if plotIt:
            fname = field.name
            pngname = "%s/Annual_%s.png" % (city, fname)
            alertTweets.maybeTweetWithSuffix(city, tweetText, fname=pngname)
    if todayMax is None:
        return todayAverageMax + 2
    return max(todayMax, todayAverageMax + 2)