Pandas: рассчитывать количество дат за год для нескольких лет - PullRequest
0 голосов
/ 21 мая 2018

У меня есть фрейм данных с записями, охватывающими несколько лет:

WarName    |     StartDate     |    EndDate
---------------------------------------------
 'fakewar1'    01-01-1990           02-02-1995
 'examplewar'  05-01-1990           03-07-1998
 (...)
 'examplewar2'  05-07-1999           06-09-2002

Я пытаюсь преобразовать этот фрейм данных в общий обзор всех войн за год, например:

  Year  |  Number_of_wars
----------------------------
  1989         0
  1990         2
  1991         2
  1992         3
  1994         2

Обычно я использовал бы что-то вроде df.groupby('year').count(), чтобы получить общее количество войн за год, но, поскольку я сейчас работаю с диапазонами, а не с установленными датами, этот подход не сработает.

В настоящее время я пишу функцию, которая генерирует список лет, а затем для каждого года в списке проверяет каждую строку в кадре данных и запускает функцию, которая проверяет, находится ли год в диапазоне дат этогоrow (возвращает True, если это так).

years = range(1816, 2006)
year_dict = {}
for year in years:
for index, row in df.iterrows():
    range = year_in_range(year, row)
    if range = True: 
       year_dict[year] = year_dict.get(year, 0) + 1

Это работает, но также кажется чрезвычайно запутанным.Так что мне было интересно, что мне не хватает?Каким будет канонический «путь панд» для решения этой проблемы?

Ответы [ 3 ]

0 голосов
/ 21 мая 2018

Используя np.unique

x,y = np.unique(sum([list(range(x.year,y.year)) for x,y in zip(df.StartDate,df.EndDate)],[]), return_counts=True)
pd.Series(dict(zip(x,y)))
Out[222]: 
1990    2
1991    2
1992    2
1993    2
1994    2
1995    1
1996    1
1997    1
1999    1
2000    1
2001    1
dtype: int64
0 голосов
/ 21 мая 2018

Другие ответы с пандами гораздо предпочтительнее, но показанный вами ответ на Python не должен был быть настолько запутанным;просто создать экземпляр и напрямую индексировать в массив:

wars = [0] * 191 # max(df['EndDate']).year - min(df['StartDate']).year + 1
yr_offset = 1816 # min(df['StartDate']).year

for _, row in df.iterrows():
  for yr in range(row['StartDate'].year-yr_offset, row['EndDate'].year-yr_offset): # or maybe (year+1)
    wars[yr] += 1
0 голосов
/ 21 мая 2018

Используйте понимание с pd.value_counts

pd.value_counts([
    d.year for s, e in zip(df.StartDate, df.EndDate)
    for d in pd.date_range(s, e, freq='Y')
]).sort_index()

1990    2
1991    2
1992    2
1993    2
1994    2
1995    1
1996    1
1997    1
1999    1
2000    1
2001    1
dtype: int64

Альтернативный

from functools import reduce

def r(t):
    return pd.date_range(t.StartDate, t.EndDate, freq='Y')

pd.value_counts(reduce(pd.Index.append, map(r, df.itertuples())).year).sort_index()

Настройка

df = pd.DataFrame(dict(
    WarName=['fakewar1', 'examplewar', 'feuxwar2'],
    StartDate=pd.to_datetime(['01-01-1990', '05-01-1990', '05-07-1999']),
    EndDate=pd.to_datetime(['02-02-1995', '03-07-1998', '06-09-2002'])
), columns=['WarName', 'StartDate', 'EndDate'])

df

      WarName  StartDate    EndDate
0    fakewar1 1990-01-01 1995-02-02
1  examplewar 1990-05-01 1998-03-07
2    feuxwar2 1999-05-07 2002-06-09
...