2015-03-31 6 views
12

В течение месяца в цифровой форме (например, 2 за февраль), как вы находите первый месяц своего квартала (например, 1 за январь)?Первый месяц квартала данного месяца в Python

Я прочитал документацию модуля datetime и документацию Pandas о своих функциях datetime, что должно быть релевантным, но я не смог найти функцию, которая решает эту проблему.

По сути, то, что я пытаюсь понять, как я мог бы произвести функцию, как один ниже, что, учитывая месяц х, выводит число, соответствующее первый месяц х «s квартал.

>> first_month_quarter(5) 
4 
+7

Недобросовестный, это будет полезно для тех, кто с подобным вопросом в будущем. Мне жаль, что я не мог найти этот ответ сам, пытаясь решить эту проблему. Политика SO отлично справляется с собственными ответами: http://stackoverflow.com/help/self-answer. –

+1

Сколько людей, по вашему мнению, будет иметь «аналогичный вопрос» *, но не сможет его тривиально решить? Кроме того, это не несправедливо - это не служба написания кода для ** никого **. Политика SO отлично сочетается с хорошими ответами ** на вопросы по теме **, это не значит, что вы можете публиковать все, что захотите. – jonrsharpe

+0

@jonrsharpe: OP также является первым ответчиком. – unutbu

ответ

19

Это простая функция отображения, которая должна преобразовать:

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 

Это может быть сделано в ряде способов с интегральным вычислением s, два из которых являются:

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

и:

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

Первый включает в себя целое деление месяца преобразуется в ноль на основе месяца, чтобы получить четверть с нуля, умножение, чтобы включить что обратно в месяц, основанный на нулевом значении (но месяц на начинается квартала), затем добавив один раз, чтобы сделать диапазон 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 

Второй раз вычитает положение в течение квартала (0, 1, 2) с самого месяца, чтобы получить начальный месяц.

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 
10

Вот ответ, предложенный TigerhawkT3. Возможно, самое скудное предложение до сих пор и, по-видимому, также было самым быстрым.

import math 

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

Например:

>> first_month_quarter(5) 
4 
+2

В стандартном модуле 'math' также есть функция« ceil », функционально эквивалентная для этого. – TigerhawkT3

+0

Хороший вопрос! Я никогда не знал, как выбирать между функциями 'ceil'' numpy' и 'math'. Не знаете, как они отличаются по эффективности и производительности. –

+1

Вы также можете использовать '((месяц-1) // 3% 4) * 3 + 1', который соглашается на месяцы между 1 и 12 (включительно) и всегда возвращает один из месяцев 1, 4, 7 или 10 для 'месяцев' менее 1 или больше 12. – unutbu

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

for i in range(1,13): 
    print i, first_month(i) 
+0

Это был бы мой ответ, если бы я не спал в то время, когда его спрашивали :-) Преобразуйте в нулевой номер месяца и просто используйте целочисленную математику. – paxdiablo

20

Это не так красиво, но если скорость важна простой список подстановки побоищ 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] 

timeit сравнение предполагает, что это примерно в два раза быстрее, чем математический подход TigerhawkT3.


тест сценарий:

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]) 

Результаты:

quarter: 3.01457574242 
firstMonthInQuarter1: 4.51578357209 
firstMonthInQuarter2: 4.01768559763 
first_month_quarter: 8.08281871176 
+9

Всегда рискованно принимать заявки на скорость, когда ответы могут появиться позже. За десять миллионов итераций ваш метод занимает 7,2 секунды, но два в моем ответе 7,0 и 7,3. На самом деле, даже «математика» работает на 7,6, так что это не так уж плохо. Не заявляя, что ваше решение не подходит (на самом деле разница между участником невелика), просто говоря, что вам может понадобиться немного вернуться к заявкам :-) В любом случае, спасибо за то, что я обучил меня «timeit», я бы не видел, что до – paxdiablo

+0

@paxdiablo true! Тем не менее, я только что повторно запускал тесты, включая ваши подходы, и видел аналогичные результаты. Использование списка более чем в два раза быстрее, чем 'math.ceil' и на 25-50% быстрее, чем целочисленная арифметика (' timeit' по умолчанию - 1 000 000 итераций). Я обновил ответ с помощью моих тестов и результатов. – jonrsharpe

2

упаковка таблицы поиска в 64-битного литерал:

def firstMonthOfQuarter(month): 
    return (0x000aaa7774441110L >> (month << 2)) & 15 
Смежные вопросы