Найти одно значение non-nan в мультииндексированном фрейме данных - PullRequest
0 голосов
/ 01 апреля 2020

РЕДАКТИРОВАТЬ: я заметил, что я слишком упростил мою проблему. Вероятно, это связано с тем, что я предполагал, что предложенные решения будут работать так же, как и мое первоначальное решение методом перебора. Я изменил мультииндекс, чтобы лучше показать свои проблемы. Приношу свои извинения тем, кто уже приложил усилия, большое спасибо!

У меня есть pandas фрейм данных, который многоиндексирован. Допустим, индекс имеет три уровня, второй уровень содержит название цвета. Я знаю, что в каждой строке все столбцы, которые имеют в индексе цвет blue, содержат NaN, за исключением одного, поэтому это выглядит так:

import pandas as pd
import numpy as np

iterables = [['bar', 'baz', 'foo', 'qux'], ["red", "blue", "green"], ['one', 'two']]
mi = pd.MultiIndex.from_product(iterables)

df = pd.DataFrame(np.random.randn(5, 24), columns=mi)
df[("bar", "blue","one")] = [2     , np.nan, np.nan, 3     , np.nan]
df[("baz", "blue","two")] = [np.nan, 4.4   , np.nan, np.nan, 5     ]
df[("qux", "blue","one")] = [np.nan, np.nan, 1     , np.nan, np.nan]

Вывод:

        bar                                               ...       qux                                             
        red           blue               green            ...       red           blue               green          
        one       two  one       two       one       two  ...       one       two  one       two       one       two
0  0.046326 -0.999092  2.0  0.073113  0.958438  0.276653  ... -0.258202 -0.772636  NaN -0.639735  1.438262 -0.033578
1  0.257776 -2.499286  NaN  0.854263 -0.037380 -0.571258  ...  1.656198 -1.110911  NaN  0.757692  0.498118  1.070371
2 -0.314146  0.941367  NaN  0.265850 -0.153231 -1.092106  ... -0.208089 -0.363624  1.0  0.046457 -2.158373  0.572496
3 -1.198977  0.605490  3.0 -0.790985  0.000563 -0.958261  ...  1.339086 -1.057270  NaN -0.355639  1.050980 -1.727684
4 -0.562230 -1.721894  NaN  0.856543 -1.137364  1.185481  ...  0.986215  1.028128  NaN -0.264889  0.571484 -0.505340

Теперь я хочу создать новый фрейм данных, который содержит значение non-nan, которое строка имеет в соответствующем столбце, а также имена других индексов этого мультииндекса.

    word number blue
0   bar  one    2.0
1   baz  two    4.4
2   qux  one    1.0
3   bar  one    3.0
4   baz  two    5.0

, то есть * 1014 Записи * и number нового кадра данных должны быть значениями, в которых исходный кадр данных имел значение, отличное от значения nan, а новый столбец blue должен содержать значения.

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

Ответы [ 3 ]

1 голос
/ 02 апреля 2020

Вы можете сложить один уровень, сохранить только столбец blue и отбросить значения NaN:

resul = df.stack(level=0)['blue'].reset_index(level=1).rename(columns={'level_1': 'number'}).dropna()

Это дает:

   number  blue
0       1   2.0
1       2   4.4
2       3   1.0
3       1   3.0
4       2   5.0

Для отредактированного вопрос, похоже, что вы хотите обрабатывать только столбцы, содержащие значения NaN, и сохранять только значения, отличные от NaN. Это может помочь:

df.loc[:,df.isna().any()].stack(level=[0,2])[['blue']].dropna()

Это дает:

           blue
0 bar one   2.0
1 baz two   4.4
2 qux one   1.0
3 bar one   3.0
4 baz two   5.0

Примечание: если вы сохраните другие столбцы, вы получите гораздо больше результатов для значений blue ...

1 голос
/ 02 апреля 2020

Если выбрать с помощью DataFrame.xs, то изменить только на DataFrame.stack, удалить первый уровень мультииндекса на reset_index с помощью drop=True и последний преобразовать Series в 2 столбцы DataFrame по Series.rename_axis и Series.reset_index:

df = (df.xs('blue', axis=1, level=1)
        .stack()
        .reset_index(level=0, drop=True)
        .rename_axis('number')
        .reset_index(name='blue'))
print (df)
   number  blue
0       1   2.0
1       2   4.4
2       3   1.0
3       1   3.0
4       2   5.0

РЕДАКТИРОВАТЬ: Решение аналогично, фильтруется не менее одного NaN с столбцы DataFrame.isna и DataFrame.any с DataFrame.loc и затем используются DataFrame.stack обоими уровнями MultiIndex :

df1 = (df.loc[:, df.isna().any()]
         .xs('blue', axis=1, level=1)
         .stack([0,1])
         .reset_index(level=0, drop=True)
         .rename_axis(('word','number'))
         .reset_index(name='blue'))

print (df1)
  word number  blue
0  bar    one   2.0
1  baz    two   4.4
2  qux    one   1.0
3  bar    one   3.0
4  baz    two   5.0
0 голосов
/ 01 апреля 2020

Вы можете проверить с двумя цепочками стека

df.stack().stack().reset_index()
   level_0 level_1  level_2     0
0        0    blue        1   2.2
1        1    blue        2   5.0
2        2    blue        1  44.0
3        3    blue        3   3.3
4        4    blue        1   1.0
5        5    blue        3   1.0
...