First month of quarter given month in Python

Question:

Given a month in numeric form (e.g., 2 for February), how do you find the first month of its respective quarter (e.g., 1 for January)?

I read through the datetime module documentation and the Pandas documentation of their datetime functions, which ought to be relevant, but I could not find a function that solves this problem.

Essentially, what I am trying to understand is how I could produce a function like the one below that, given month x, outputs the number corresponding to the first month of x‘s quarter.

>> first_month_quarter(5)
4
Asked By: Gyan Veda

||

Answers:

Here is an answer suggested by TigerhawkT3. Perhaps the leanest suggestion so far and, apparently, also the fastest.

import math

def first_month_quarter(month):
    return int(math.ceil(month / 3.)) * 3 - 2

For example:

>> first_month_quarter(5)
4
Answered By: Gyan Veda
def first_month(month):
    return (month-1)//3*3+1

for i in range(1,13):
    print i, first_month(i)
Answered By: Robᵩ

It’s not so pretty, but if speed is important a simple list lookup slaughters math:

def quarter(month, quarters=[None, 1, 1, 1, 4, 4, 4,
                             7, 7, 7, 10, 10, 10]):
    """Return the first month of the quarter for a given month."""
    return quarters[month]

A timeit comparison suggests this is about twice as fast as TigerhawkT3’s mathematical approach.


Test script:

import math

def quarter(month, quarters=[None, 1, 1, 1, 4, 4, 4,
                             7, 7, 7, 10, 10, 10]):
    """Return the first month of the quarter for a given month."""
    return quarters[month]

def firstMonthInQuarter1(month):
    return (month - 1) // 3 * 3 + 1

def firstMonthInQuarter2(month):
    return month - (month - 1) % 3

def first_month_quarter(month):
    return int(math.ceil(month / 3.)) * 3 - 2

if __name__ == '__main__':
    from timeit import timeit
    methods = ['quarter', 'firstMonthInQuarter1', 'firstMonthInQuarter2',
               'first_month_quarter']
    setup = 'from __main__ import {}'.format(','.join(methods))
    results = {method: timeit('[{}(x) for x in range(1, 13)]'.format(method),
                              setup=setup)
               for method in methods}
    for method in methods:
        print '{}:t{}'.format(method, results[method])

Results:

quarter:    3.01457574242
firstMonthInQuarter1:   4.51578357209
firstMonthInQuarter2:   4.01768559763
first_month_quarter:    8.08281871176
Answered By: jonrsharpe

It’s a simple mapping function that needs to convert:

1 2 3 4 5 6 7 8 9 10 11 12
           |
           V
1 1 1 4 4 4 7 7 7 10 10 10

This can be done in a number of ways with integral calculations, two of which are:

def firstMonthInQuarter(month):
    return (month - 1) // 3 * 3 + 1

and:

def firstMonthInQuarter(month):
    return month - (month - 1) % 3

The first involves integer division of the month converted to a zero-based month to get the zero-based quarter, multiplication to turn that back into a zero-based month (but the month at the start of the quarter), then adding one again to make the range 1..12.

month  -1  //3  *3  +1
-----  --  ---  --  --
    1   0    0   0   1
    2   1    0   0   1
    3   2    0   0   1
    4   3    1   3   4
    5   4    1   3   4
    6   5    1   3   4
    7   6    2   6   7
    8   7    2   6   7
    9   8    2   6   7
   10   9    3   9  10
   11  10    3   9  10
   12  11    3   9  10

The second just subtracts the position within a quarter (0, 1, 2) from the month itself to get the starting month.

month(a)  -1  %3(b)  a-b
--------  --  -----  ---
       1   0      0    1
       2   1      1    1
       3   2      2    1
       4   3      0    4
       5   4      1    4
       6   5      2    4
       7   6      0    7
       8   7      1    7
       9   8      2    7
      10   9      0   10
      11  10      1   10
      12  11      2   10
Answered By: paxdiablo

Packing a lookup table into a 64-bit literal:

# Python 2
def firstMonthOfQuarter(month):
    return (0x000aaa7774441110L >> (month << 2)) & 15

# Python 3
def firstMonthOfQuarter(month):
    return (0x000aaa7774441110 >> (month << 2)) & 15
Answered By: samgak
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.