Установка переменных столбцов для подмножества строк в мультииндексном фрейме данных Pandas - PullRequest
0 голосов
/ 14 мая 2018

Я хочу переназначить значения в определенных строках и различных многоиндексных столбцах большого кадра данных pandas, df, на значения, отличные от NaN, которые были рассчитаны и сохранены в немного меньшем замаскированном подмножестве кадра данных, df_sub.

df =
    A                                                           B        
      0     1     2     3     4     5     6     7     8     9      0     1     2     3     4     5     6     7     8     9        
0   1.0   2.0   3.0   4.0   5.0   6.0   7.0   8.0   9.0  10.0  -51.0 -50.0 -49.0 -48.0 -47.0 -46.0 -45.0 -44.0 -43.0 -42.0   
1  11.0  12.0  13.0  14.0  15.0  16.0  17.0  18.0  19.0  20.0  -41.0 -40.0 -39.0 -38.0 -37.0 -36.0 -35.0 -34.0 -33.0 -32.0   
2  21.0  22.0  23.0  24.0  25.0  26.0  27.0  28.0  29.0  30.0  -31.0 -30.0 -29.0 -28.0 -27.0 -26.0 -25.0 -24.0 -23.0 -22.0   
3  31.0  32.0  33.0  34.0  35.0  36.0  37.0  38.0  39.0  40.0  -21.0 -20.0 -29.0 -28.0 -27.0 -26.0 -25.0 -24.0 -23.0 -22.0   
4  41.0  42.0  43.0  44.0  45.0  46.0  47.0  48.0  49.0  50.0  -11.0 -10.0  -9.0  -8.0  -7.0  -6.0  -5.0  -4.0  -3.0  -2.0  

df_sub =
      0     1     2     3     4     5     6     7     8     9 
1    NaN   NaN   NaN   NaN   NaN   0.3   0.2   0.1   NaN   NaN
3    NaN   NaN   NaN   0.6   0.9   0.7   NaN   NaN   NaN   NaN

Моя цель - получить результат, показанный ниже, для df.loc [:, 'B'], где ненулевые значения в df_sub заменяют соответствующую строку и столбцы df (i.e., df.loc[1, pd.IndexSlice['B', 5:7]] = df_sub.loc[1, 5:7] and df.loc[3, pd.IndexSlice['B', 3:5]] = df_sub.loc[3, 3:5]):

df.loc[:,'B'] =
      0     1     2     3     4     5     6     7     8     9
0 -51.0 -50.0 -49.0 -48.0 -47.0 -46.0 -45.0 -44.0 -43.0 -42.0
1 -41.0 -40.0 -39.0 -38.0 -37.0   0.3   0.2   0.1 -33.0 -32.0
2 -31.0 -30.0 -29.0 -28.0 -27.0 -26.0 -25.0 -24.0 -23.0 -22.0
3 -21.0 -20.0 -19.0   0.6   0.9   0.7 -15.0 -14.0 -13.0 -12.0
4 -11.0 -10.0  -9.0  -8.0  -7.0  -6.0  -5.0  -4.0  -3.0  -2.0

Однако, получая желаемые значения, я получаю NaNs:

df.loc[:,'B'] =
      0     1     2     3     4     5     6     7     8     9
0 -51.0 -50.0 -49.0 -48.0 -47.0 -46.0 -45.0 -44.0 -43.0 -42.0
1 -41.0 -40.0 -39.0 -38.0 -37.0   NaN   NaN   NaN -33.0 -32.0
2 -31.0 -30.0 -29.0 -28.0 -27.0 -26.0 -25.0 -24.0 -23.0 -22.0
3 -21.0 -20.0 -19.0   NaN   NaN   NaN -15.0 -14.0 -13.0 -12.0
4 -11.0 -10.0  -9.0  -8.0  -7.0  -6.0  -5.0  -4.0  -3.0  -2.0

Мой простой пример кода приведен ниже. Исходя из диагностики, похоже, что все ведет себя так, как ожидалось: 1) значения non-nan и их индексы из df_sub определены для каждой строки df_sub, 2) срезы исходного df кажутся правильными, и 3) назначение выполняется без претензий и предупреждений «установка копии».

  1. Как лучше всего достичь моей цели?
  2. Почему это не получается?
  3. Есть ли более компактный и эффективный способ выполнения заданий?

Упрощенный пример:

# Create data for example case
idf = pd.MultiIndex.from_product([['A', 'B'], np.arange(0,10)])
df = pd.DataFrame(np.concatenate((np.arange(1.,51.).reshape(5,10), 
                  np.arange(-51., -1.).reshape(5,10)), axis=1), 
                  index=np.arange(0,5), columns=idf)
df_sub = pd.DataFrame([[np.nan, np.nan, np.nan, np.nan, np.nan, 0.5, 0.6, 0.7, np.nan, np.nan], 
                      [np.nan, np.nan, np.nan, 0.3, 0.4, 0.5, np.nan, np.nan, np.nan, np.nan]],
                      index=[1,3], columns=np.arange(0,10))
dfsub_idx = df_sub.index

# Perform assignments
for (idx, row) in df_sub.iterrows() :
   arr = row.index[~row.isnull()] 
   print 'row {}: \n{}'.format(idx, row)
   print 'non-nan indices: {}\n'.format(arr)
   print 'df before mod: \n{}'.format(df.loc[idx, pd.IndexSlice['B', arr.tolist()]])
   df.loc[idx, pd.IndexSlice['B', arr.tolist()]] = row[arr] 
   print 'df after mod: \n{}'.format(df.loc[idx, pd.IndexSlice['B', arr.tolist()]])

Ответы [ 2 ]

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

Встроенный с pandas.DataFrame.align и pandas.DataFrame.fillna

Используя аргумент level

pd.DataFrame.fillna(*df_sub.align(df, level=1))

      A                                                           B                                                      
      0     1     2     3     4     5     6     7     8     9     0     1     2     3     4     5     6     7     8     9
0   1.0   2.0   3.0   4.0   5.0   6.0   7.0   8.0   9.0  10.0 -51.0 -50.0 -49.0 -48.0 -47.0 -46.0 -45.0 -44.0 -43.0 -42.0
1  11.0  12.0  13.0  14.0  15.0   0.5   0.6   0.7  19.0  20.0 -41.0 -40.0 -39.0 -38.0 -37.0   0.5   0.6   0.7 -33.0 -32.0
2  21.0  22.0  23.0  24.0  25.0  26.0  27.0  28.0  29.0  30.0 -31.0 -30.0 -29.0 -28.0 -27.0 -26.0 -25.0 -24.0 -23.0 -22.0
3  31.0  32.0  33.0   0.3   0.4   0.5  37.0  38.0  39.0  40.0 -21.0 -20.0 -19.0   0.3   0.4   0.5 -15.0 -14.0 -13.0 -12.0
4  41.0  42.0  43.0  44.0  45.0  46.0  47.0  48.0  49.0  50.0 -11.0 -10.0  -9.0  -8.0  -7.0  -6.0  -5.0  -4.0  -3.0  -2.0

На месте с update

df.update(df_sub.align(df, level=1)[0])

Разъяснение

Это:

pd.DataFrame.fillna(*df_sub.align(df, level=1))

эквивалентно

a, b = df_sub.align(df, level=1)
a.fillna(b)
# Or pd.DataFrame.fillna(a, b)
0 голосов
/ 14 мая 2018

Вы должны добавить values в конце df_sub после .iloc

df.loc[1, pd.IndexSlice['B', 5:7]] = df_sub.loc[1, 5:7].values 
df.loc[3, pd.IndexSlice['B', 3:5]] = df_sub.loc[3, 3:5].values
...