Сгруппируйте данные в csv по сезонам и годам, используя python и pandas

Это расширение более раннего вопроса .

Я хочу использовать Pandas и Python для перебора моего файла .csv и группировки данных по сезонам (и годам), вычисляя среднее значение для каждого сезона в году. В настоящее время ежеквартальный сценарий работает с января по март, с апреля по июнь и т. д. Я хочу, чтобы времена года соотносились с месяцами по

- 11: 'Winter', 12: 'Winter', 1: 'Winter', 2: 'Spring', 3: 'Spring', 4: 'Spring', 5: 'Summer', 6: 'Summer', 7: 'Summer', \ 8: 'Autumn', 9: 'Autumn', 10: 'Autumn'

У меня есть следующие данные:

Date,HAD
01/01/1951,1
02/01/1951,-0.13161201
03/01/1951,-0.271796132
04/01/1951,-0.258977158
05/01/1951,-0.198823057
06/01/1951,0.167794502
07/01/1951,0.046093808
08/01/1951,-0.122396694
09/01/1951,-0.121824587
10/01/1951,-0.013002463

...

вплоть до

20/12/2098,62.817
21/12/2098,59.998
22/12/2098,50.871
23/12/2098,88.405
24/12/2098,81.154
25/12/2098,83.617
26/12/2098,120.675
27/12/2098,273.795
28/12/2098,316.324
29/12/2098,260.951
30/12/2098,198.505
31/12/2098,150.755

Это код из предыдущего вопроса, который работает

import pandas as pd
import os
import re

lookup = {
    11: 'Winter',
    12: 'Winter',
    1: 'Winter',
    2: 'Spring',
    3: 'Spring',
    4: 'Spring',
    5: 'Summer',
    6: 'Summer',
    7: 'Summer',
    8: 'Autumn',
    9: 'Autumn',
    10: 'Autumn'
}

os.chdir('C:/Users/n-jones/testdir/output/')

for fname in os.listdir('.'):
    if re.match(".*csv$", fname):
        data = pd.read_csv(fname, parse_dates=[0], dayfirst=True)
        data['Season'] = data['Date'].apply(lambda x: lookup[x.month])
        data['count'] = 1
        data = data.groupby(['Season'])['HAD', 'count'].sum()
        data['mean'] = data['HAD'] / data['count']
        data.to_csv('C:/Users/n-jones/testdir/season/' + fname)

Я хочу, чтобы мой выходной CSV-файл был:

Autumn 1951, Mean, Winter 1951/52, Mean, Spring 1952, Mean, Summer 1952, Mean,
Autumn 1952, Mean, Winter 1952/53, Mean, Spring 1953, Mean, Summer 1953, Mean,

и так далее...

Надеюсь, это имеет смысл.

Заранее спасибо!


person Nick Jones    schedule 27.03.2014    source источник
comment
Не могли бы вы загрузить больший образец данных (например, с помощью pastebin или подобного)?   -  person Steinar Lima    schedule 27.03.2014
comment
dropbox.com/s/cjw4u52pmmd6uep/NJDATA.csv ссылка на файл csv файл - это нормально?   -  person Nick Jones    schedule 27.03.2014


Ответы (3)


Это идеальный пример для ситуации, когда itertools.groupby твой лучший друг!

Пожалуйста, простите меня за то, что я не расширил ваш ответ, но я не слишком хорошо знаком с пандами, поэтому я решил использовать модуль csv.

Написав два метода для группировки данных (get_season и get_year), нужно всего лишь перебрать группы и записать данные в новый CSV-файл.

import csv
from datetime import datetime
from itertools import groupby

LOOKUP_SEASON = {
    11: 'Winter',
    12: 'Winter',
    1: 'Winter',
    2: 'Spring',
    3: 'Spring',
    4: 'Spring',
    5: 'Summer',
    6: 'Summer',
    7: 'Summer',
    8: 'Autumn',
    9: 'Autumn',
    10: 'Autumn'
}


def get_season(row):
    date = datetime.strptime(row[0], '%d/%m/%Y')
    season = LOOKUP_SEASON[date.month]
    if season == 'Winter':
        if date.month == 1:
            last_year, next_year = date.year - 1, date.year
        else:
            last_year, next_year = date.year, date.year + 1
        return '{} {}/{}'.format(season, last_year, next_year)
    else:
        return '{} {}'.format(season, date.year)


def get_year(row):
    date = datetime.strptime(row[0], '%d/%m/%Y')
    if date.month < 8:
        return date.year - 1
    else:
        return date.year


with open('NJDATA.csv') as data_file, open('outfile.csv', 'wb') as out_file:
    headers = next(data_file)
    reader = csv.reader(data_file)
    writer = csv.writer(out_file)

    # Loop over groups distinguished by the "year" from Autumn->Summer,
    # defined by the `get_year` function
    for year, seasons in groupby(reader, get_year):
        mean_data = []
        # Loop over the data in the current year, grouped by season, defined
        # by the get_season method. Since the required "season string"
        # (e.g Autumn 1952) can be used as an identifier for the seasons,
        # the `get_season` method returns the specific string which is used
        # in the output, so you don't have to compile that one more time
        # inside the for loops
        for season_str, iter_data in groupby(seasons, get_season):
            data = list(iter_data)
            mean = sum([float(row[1]) for row in data]) / len(data)
            # Use the next line instead if you want to control the precision
            #mean = '{:.3f}'.format(sum([float(row[1]) for row in data]) / len(data))
            mean_data.extend([season_str, mean])
        writer.writerow(mean_data)

Основная идея состоит в том, чтобы сначала сгруппировать данные по годам (осень -> лето), а затем снова сгруппировать эти данные по сезонам. Функция groupby принимает два аргумента; одна последовательность и одна функция. Он перебирает последовательность, и всякий раз, когда возвращаемое значение предоставленной функции изменяется, предшествующие данные рассматриваются как отдельная группа.

Рассмотрим этот образец данных:

01/01/1951,1
02/01/1951,-0.13161201
01/04/1951,1
02/04/1951,-0.13161201
03/04/1951,-0.271796132
04/06/1951,-0.258977158
05/06/1951,-0.198823057
06/08/1951,0.167794502
...
09/02/1952,-0.121824587

Первый вызов groupby группирует данные на основе вашего определения года (определенного в get_year), предоставляя следующие группы данных:

# get_year returns 1950
01/01/1951,1
...
05/06/1951,-0.198823057

# get_year returns 1951 
06/08/1951,0.167794502
...
09/02/1952,-0.121824587

Следующий метод groupby группирует каждую из вышеуказанных групп по сезону (определенному в get_season). Рассмотрим первую группу:

# get_season returns 'Winter 1950/1951'
01/01/1951,1
02/01/1951,-0.13161201

# get_season returns 'Spring 1951'
01/04/1951,1
02/04/1951,-0.13161201
03/04/1951,-0.271796132

# get_season returns 'Summer 1951'
04/06/1951,-0.258977158
05/06/1951,-0.198823057
person Steinar Lima    schedule 27.03.2014
comment
спасибо, стейнар, сейчас посмотрю. Я загрузил пример csv для вашего просмотра. - person Nick Jones; 27.03.2014
comment
Спасибо, Стенинар, есть несколько моментов, мне было интересно, не могли бы вы уточнить или, может быть, прокомментировать свой код? для года, сезонов в groupby(reader, get_year): mean_data = [] для Season_str, iter_data в groupby(seasons, get_season): data = list(iter_data) mean = sum([float(row[1]) для строки в данных ]) / len(data) mean_data.extend([season_str, mean]) - person Nick Jones; 27.03.2014
comment
Еще один бонусный вопрос: у меня есть 13 столбцов данных, например, Date Had1 Had 2 Had3 ... легко ли это включить? - person Nick Jones; 27.03.2014
comment
Большое спасибо, steinar, я придумал, как добавить дополнительные столбцы данных. Это гениально :) - person Nick Jones; 27.03.2014
comment
Привет, Стейнар. Извинения. Я не знал, что вы не можете принять два ответа - один быстрый вопрос. Я ошибся при поиске, должно быть LOOKUP_SEASON = { 12: 'Зима', 1: 'Зима', 2: 'Зима', 3: 'Весна', 4: 'Весна', 5: 'Весна', 6 : 'Лето', 7: 'Лето', 8: 'Лето', 9: 'Осень', 10: 'Осень', 11: 'Осень', } Я возился с кодом, поэтому думаю, что он работает. но он дважды генерирует зимний результат. Спасибо Ник - person Nick Jones; 28.03.2014

Вот простое решение:

import pandas as pd

def year_and_season(x):
    season = lookup[x.month]
    year = x.year
    if x.month == 12:
        year += 1
    return (year, season)

data = pd.read_csv('example.csv', index_col=0, parse_dates=[0], dayfirst=True)
yearsAndSeason = data.groupby(year_and_season).mean()
yearsAndSeason.to_csv('results.csv')

Обратите внимание, что столбец индекса при чтении был установлен на дату, поэтому мы можем получить доступ к его полям непосредственно в функции groupBy. Там мы возвращаем кортеж с годом и сезоном. Вы можете вызвать функцию mean напрямую вместо sum.

results.csv выглядит не совсем так, как вы ожидаете, потому что ключи печатаются в виде кортежа, но, возможно, вы справитесь с этой частью. Вот как это выглядит у меня...

$ cat results.csv
,Mean
"(1951, 'Winter')",0.009545620900000005
"(2099, 'Winter')",145.65558333333334
person logc    schedule 27.03.2014
comment
Это работает отдельно от зимнего сезона, потому что декабрь нужно взять из предыдущего года, если это имеет смысл? - person Nick Jones; 27.03.2014
comment
Обратите внимание, что теперь строки с декабря 2098 года отображаются как 2099. Интересно, у вас есть результаты на далекое будущее! :) - person logc; 27.03.2014
comment
Все еще не уверен, что это работает, так как pandas необходимо получить значения, связанные с предыдущим годом. Зимнее значение по-прежнему неверно. - person Nick Jones; 27.03.2014
comment
Я проверил, и этот код работает, а другой работает неправильно. Я поиграю с обоими, посмотрю, как все это работает. - person Nick Jones; 27.03.2014

Я столкнулся с такой же проблемой и обнаружил, что для этого можно использовать метод повторной выборки, просто используя параметр 3M (на 3 месяца).

Я обнаружил это благодаря этому веб-сайту, который приводит пример, связанный с вопросом http://earthpy.org/time_series_analysis_with_pandas_part_2.html< /а>.

Если у вас есть фрейм данных с индексом в виде объекта pandas datetime, все, что вам нужно сделать, это попросить выполнить повторную выборку каждые 3 месяца.

In [108]:
data.head()
Out[108]:
         Sample Measurement
              mean
Date Local  
2006-01-01  50.820833
2006-01-02  41.900000
2006-01-03  45.870833
2006-01-04  50.850000
2006-01-05  37.116667

In[109]:
#88 in order to beginn the resampling in march
wm = data[88:].resample('3M', closed='left')
wm.head()
out[109]:
         Sample Measurement
              mean
Date Local  
2006-05-31  7.153622
2006-08-31  5.883025
2006-11-30  11.619724
2007-02-28  21.105789
2007-05-31  8.105313

Это в моем наборе данных с ежедневными значениями, я потерял данные за первые три месяца, но я думаю, что это действительно простой способ поиграть с сезонами.

person User18981898198119    schedule 18.09.2015