Панды создают идеальную панель данных, добавляя пустые строки в зависимости от условий

Я ищу способ добавить пустые строки в фрейм данных, если выполняются два условия. Условия заключаются в том, что если идентификатор индекса не найден в конкретном году, код добавит пустую строку с идентификатором индекса и годом, но другие столбцы будут пустыми. Конечная цель - создать идеальный набор панельных данных, где каждое наблюдение представлено 7 раз (в зависимости от года), хотя могут быть данные из некоторых наблюдений, например. 1 или 3 раза (это не постоянно, но время от времени меняется). В противном случае эти отсутствующие строки данных были бы пустыми, за исключением индекса «ID» и года.

Вот пример того, как сейчас выглядит мой фрейм данных all_data:

ID      Year      Data1      Data2
345     2010        3          2
345     2011        1          4
345     2012        5          2
345     2013        3          1
345     2014        3          1
345     2015        3          1
345     2016        3          1
123     2010        1          1
123     2012        0          2
123     2016        0          2

Вот пример того, что я ищу.

ID      Year      Data1      Data2
345     2010        3          2
345     2011        1          4
345     2012        5          2
345     2013        3          1
345     2014        3          1
345     2015        3          1
345     2016        3          1
123     2010        1          1
123     2011                  
123     2012        0          2
123     2013
123     2014
123     2015
123     2016        0          2

У меня более 200 наблюдений и 20 столбцов данных, поэтому выполнение этого вручную занимает слишком много времени. Это то, что я пробовал, но это не работает. Он возвращает тот же фрейм данных и не добавляет пустых строк. «Отсутствующие» - это список, содержащий все уникальные идентификаторы, которые можно найти во фрейме данных all_data.

missing = ['345', '123']
sub_dfs = []
for year in [ 2010, 2011, 2012, 2013, 2014, 2015, 2016 ]:
    sub_df = all_data.loc[ all_data[ 'Year' ] == year ].copy()
    if( year == 2010):
        sub_df.set_index( 'ID', inplace=True)
        sub_df.reindex(sub_df.index.union(missing))
    if (year == 2011):
        sub_df.set_index('ID', inplace=True)
        sub_df.reindex(sub_df.index.union(missing))
    if (year == 2012):
        sub_df.set_index('ID', inplace=True)
        sub_df.reindex(sub_df.index.union(missing))
    if (year == 2013):
        sub_df.set_index('ID', inplace=True)
        sub_df.reindex(sub_df.index.union(missing))
    if (year == 2014):
        sub_df.set_index('ID', inplace=True)
        sub_df.reindex(sub_df.index.union(missing))
    if (year == 2015):
        sub_df.set_index('ID', inplace=True)
        sub_df.reindex(sub_df.index.union(missing))
    if (year == 2016):
        sub_df.set_index('ID', inplace=True)
        sub_df.reindex(sub_df.index.union(missing))
    sub_dfs.append(sub_df)

new_data = pd.concat(sub_dfs)

Заранее спасибо за вашу помощь!


person hexi    schedule 13.07.2018    source источник


Ответы (2)


Используйте reindex от Multiindex, созданного _ 3_ всеми _ 4_ значения ID с np.arange минимальным и максимальным years:

mux = pd.MultiIndex.from_product([df['ID'].unique(), 
                                  np.arange(df['Year'].min(), df['Year'].max() + 1)],
                                  names=['ID','Year'])

df =  df.set_index(['ID','Year']).reindex(mux).reset_index()
print (df)
     ID  Year  Data1  Data2
0   345  2010    3.0    2.0
1   345  2011    1.0    4.0
2   345  2012    5.0    2.0
3   345  2013    3.0    1.0
4   345  2014    3.0    1.0
5   345  2015    3.0    1.0
6   345  2016    3.0    1.0
7   123  2010    1.0    1.0
8   123  2011    NaN    NaN
9   123  2012    0.0    2.0
10  123  2013    NaN    NaN
11  123  2014    NaN    NaN
12  123  2015    NaN    NaN
13  123  2016    0.0    2.0
person jezrael    schedule 13.07.2018

jezrael всегда быстрее, но я пытаюсь выучить панд здесь, так что вот моя попытка;)

Я использую метод повторной выборки: вы хотите заполнить пробелы, изменив отображение данных на годовой интервал начала ('AS')

Сначала преобразуйте столбец 'Year' в pandas datetime и установите его как индекс

df.Year = pd.to_datetime(df.Year, format="%Y")
df = df.set_index('Year')

Затем я обрабатываю каждый уникальный идентификатор отдельно и создаю новый выходной DataFrame.

IDs = df.ID.unique()
newDf = pd.DataFrame()

Цикл обработки

for ID in IDs:
    # resample to annual start (although end would also be OK)
    temp = df[df.ID==ID].resample('AS').sum()
    # fill in the blanks, now 0, with the wanted data
    temp[temp.ID==0] = pd.DataFrame({'ID':ID, 'Data1':'', 'Data2':''},
        index=temp[temp.ID==0].index)
    # concat this new data with the output frame
    newDf = pd.concat([newDf, temp])

Наконец, очистите вывод, удалив индекс и преобразовав дату и время обратно в строку

newDf = newDf.reset_index()
newDf.Year = newDf.Year.dt.strftime('%Y')

Результат:

    Year   ID Data1 Data2
0   2010  345     3     2
1   2011  345     1     4
2   2012  345     5     2
3   2013  345     3     1
4   2014  345     3     1
5   2015  345     3     1
6   2016  345     3     1
7   2010  123     1     1
8   2011  123            
9   2012  123     0     2
10  2013  123            
11  2014  123            
12  2015  123            
13  2016  123     0     2
person JHBonarius    schedule 13.07.2018