Используется groupby для выбора самых последних данных, хотите добавить столбец, который возвращает дату данных

Изначально у меня был кадр данных, который выглядел так:

                                  industry    population %of rural land
        country       date        
        Australia     2017-01-01  NaN         NaN        NaN
                      2016-01-01  24.327571   18.898304  12
                      2015-01-01  25.396251   18.835267  12
                      2014-01-01  27.277007   18.834835  13
        United States 2017-01-01  NaN         NaN        NaN
                      2016-01-01  NaN         19.028231  NaN
                      2015-01-01  20.027274   19.212860  NaN
                      2014-01-01  20.867359   19.379071  NaN

Я применил следующий код, который извлек самые последние данные для каждого из столбцов для каждой из стран и привел к следующему набору данных:

df = df.groupby(level=0).first()

               industry  population  %of rural land
country                             
Australia      24.327571   18.898304 12
United States  20.027274   19.028231 NaN

Есть ли способ добавить столбец, который также показывает год данных? а в случае, когда год отличается для одной и той же страны, вернуть самый старый год данных в новом фрейме данных? Таким образом, для Австралии это будет 2016 год, а для США — 2015 год. В идеале кадр данных должен выглядеть так:

               year      industry  population  %of rural land
country                             
Australia      2016      24.327571   18.898304 12
United States  2015      20.027274   19.028231 NaN

person Susan Chen    schedule 05.12.2017    source источник


Ответы (1)


Я думаю, вам нужно на first год не NaN строк создать помощник Series с помощью dropna и затем:

s = df.dropna().reset_index(level=1)['date'].dt.year.groupby(level=0).first()
df1 = df.groupby(level=0).first()
df1.insert(0, 'year', df1.rename(s).index)
#alternative
#df1.insert(0, 'year', df1.index.to_series().map(s))
print (df1)
               year   industry  population
country                                   
Australia      2016  24.327571   18.898304
United States  2015  20.027274   19.028231

Другое решение с добавлением столбца NaNs в date и получением последних лет на dt.year:

df1 = (df.reset_index(level=1)
        .assign(date=lambda x: x['date'].where(df.notnull().all(1).values))
        .groupby(level=0).first()
        .assign(date=lambda x: x['date'].dt.year)
        .rename(columns={'date':'year'}))
print (df1)
               year   industry  population
country                                   
Australia      2016  24.327571   18.898304
United States  2015  20.027274   19.028231

РЕДАКТИРОВАТЬ:

def f(x):
    #check NaNs
    m = x.isnull()
    #remove all NaNs columns 
    m = m.loc[:, ~m.all()]
    #first index value of non NaNs rows
    m = m[~m.any(1)].index[0][1].year
    return (m)

s = df.groupby(level=0).apply(f)
print (s)
country
Australia        2016
United States    2015
dtype: int64

df1 = df.groupby(level=0).first()
df1.insert(0, 'year', df1.rename(s).index)
#alternative
#df1.insert(0, 'year', df1.index.to_series().map(s))
print (df1)
               year   industry  population  %of rural land
country                                                   
Australia      2016  24.327571   18.898304            12.0
United States  2015  20.027274   19.028231             NaN
person jezrael    schedule 05.12.2017
comment
Первое решение возвращает название страны, когда год равен NaN, но второе решение возвращает NaN, когда это NaN, чего я и хочу! Годы идут как поплавки (2016.00000). Где во втором решении я могу это изменить? - person Susan Chen; 05.12.2017
comment
Может быть, какая-то проблема с версиями панд, вы используете последнюю 0.21.0? - person jezrael; 05.12.2017
comment
у меня сейчас 0.21.0. Кроме того, я получаю эту ошибку: можно использовать только аксессор .dt со значениями, подобными дате и времени. - person Susan Chen; 05.12.2017
comment
Тогда проще первое решение, используйте s = pd.to_datetime(df.dropna().reset_index(level=1)['date']).dt.year.groupby(level=0).first() - person jezrael; 05.12.2017
comment
а для второго нужно изменить .assign(date=lambda x: x['date'].dt.year) на .assign(date=lambda x: pd.to_datetime(x['date']).dt.year) - person jezrael; 05.12.2017
comment
Давайте продолжим обсуждение в чате. - person Susan Chen; 06.12.2017
comment
Есть какая-то проблема? - person jezrael; 06.12.2017
comment
да, я думаю, что NaN возвращается за год, когда в стране есть NaN в результате некоторых переменных. так, например, для Австралии промышленность - это NaN, а население - нет, год для Австралии будет "NaN" - person Susan Chen; 06.12.2017
comment
Итак, с примерами данных вы получаете неправильный результат? Или лучше задать вопрос: нужен ли вам лучший год по странам после удаления всех строк с NaN? Или логика другая? - person jezrael; 06.12.2017
comment
Я извлекаю данные по каждой стране за последние пять лет, и для некоторых переменных нет данных за последние пять лет, поэтому самым последним значением этих переменных является NaN. Меня это устраивает, но похоже, что если в стране есть несколько NaN, приведенный выше код вернет NaN. Я хочу, чтобы код по-прежнему смотрел на годы доступных переменных. - person Susan Chen; 06.12.2017
comment
Не уверен, что понял. Если все значения являются NaN, например, рассматриваемый образец со всеми NaN, какой желаемый результат? Я предполагаю, что для столбцов NaNs и для Yers top, первых лет по странам? - person jezrael; 06.12.2017
comment
Я отредактировал кадры данных в вопросе выше, чтобы дать вам пример того, что я имею в виду. Если я применю код, который вы мне дали, год в последнем фрейме данных будет NaN, потому что «% сельской земли — это NaN». Как я могу игнорировать значения NaN и вернуть год для доступных данных для каждой страны? - person Susan Chen; 06.12.2017
comment
Итак, можно ли сначала удалить все столбцы nans, а затем применить мое решение? Затем используйте первый шаг df = df.dropna(axis=1, how='all'), проверьте также это. - person jezrael; 06.12.2017
comment
Извините, это был плохой пример. Я изменил его, чтобы вы могли понять, что я имею в виду. Я не могу удалить столбец, потому что он может быть нулевым не для всех стран. - person Susan Chen; 06.12.2017
comment
Эта ошибка возвращается: индекс 0 выходит за пределы оси 0 с размером 0 - person Susan Chen; 06.12.2017
comment
Я пытаюсь проверить это, и я не могу смоделировать вашу ошибку. Данные конфиденциальны? Если нет, можно ли отправить мне csv на мою электронную почту в моем профиле? - person jezrael; 08.12.2017