Сохранение индексов в фрейме данных Pandas с определенным количеством не-NaN целых чисел

#python #pandas #dask

#питон #pandas #dask

Вопрос:

Допустим, у меня есть следующий фрейм данных:

 df1 = pd.DataFrame(data    = [1,np.nan,np.nan,1,1,np.nan,1,1,1], 
                   columns = ['X'], 
                   index   = ['a', 'a', 'a', 
                              'b', 'b', 'b',
                              'c', 'c', 'c'])
print(df1)
     X
a  1.0
a  NaN
a  NaN
b  1.0
b  1.0
b  NaN
c  1.0
c  1.0
c  1.0
 

Я хочу сохранить только те индексы, которые имеют 2 или более записей, отличных от NaN. В этом случае записи ‘a’ имеют только одно значение, отличное от NaN, поэтому я хочу отбросить его, и мой результат будет:

      X
b  1.0
b  1.0
b  NaN
c  1.0
c  1.0
c  1.0
 

Каков наилучший способ сделать это? В идеале я хочу что-то, что работает и с Dask, хотя обычно, если это работает с Pandas, это работает и в Dask.

Ответ №1:

Давайте попробуем filter

 out = df.groupby(level=0).filter(lambda x : x.isna().sum()<=1)
     X
b  1.0
b  1.0
b  NaN
c  1.0
c  1.0
c  1.0
 

Или мы делаем isin

 df[df.index.isin(df.isna().sum(level=0).loc[lambda x : x['X']<=1].index)]
     X
b  1.0
b  1.0
b  NaN
c  1.0
c  1.0
c  1.0
 

Комментарии:

1. Оба они работают в Pandas, однако, похоже, это редкий случай (по крайней мере, по моему опыту), когда он не работает в Dask. Ни функция «фильтр», ни суммирование на основе уровней, по-видимому, не реализованы в Dask. Однако я отмечу ответ как решение, поскольку он в основном отвечал на поставленный вопрос. Изначально я решил спросить о Pandas вместо Dask, поскольку у людей обычно больше опыта работы с Pandas, и опять же решения обычно взаимозаменяемы…

Ответ №2:

В качестве другого варианта давайте попробуем фильтровать через GroupBy.transform и логическое индексирование:

 df1[df1['X'].isna().groupby(df1.index).transform('sum') <= 1]

     X
b  1.0
b  1.0
b  NaN
c  1.0
c  1.0
c  1.0
 

Или почти таким же образом,

 df1[df1.assign(X=df1['X'].isna()).groupby(level=0)['X'].transform('sum') <= 1]

     X
b  1.0
b  1.0
b  NaN
c  1.0
c  1.0
c  1.0
 

Возможно, у вас есть хороший шанс заставить это работать и с Dask.

Комментарии:

1. Оба они работают в Pandas, но приводят к следующей ошибке для Dask: ValueError: Not all divisions are known, can't align partitions. Please use set_index` для установки индекса.’

Ответ №3:

Я новичок в dask, просмотрел некоторые примеры и документы, однако, похоже, работает следующее;

 from dask import dataframe as dd 
sd = dd.from_pandas(df1, npartitions=3)
 

 #converts X to boolean checking for isna() and the groupby on index and sum
s = sd.X.isna().groupby(sd.index).sum().compute()

#using the above we can boolean index to check if sum is less than 2 , then use loc

out_dd = sd.loc[list(s[s<2].index)]
 

 out_dd.head(6,npartitions=-1)

     X
b  1.0
b  1.0
b  NaN
c  1.0
c  1.0
c  1.0
 

Комментарии:

1. Спасибо за публикацию этого. Я смог использовать часть вашего кода для тестирования своего кода на Dask. И ваш код работает отлично

2. Поскольку OP так и не ответил, я назначу награду за этот ответ, поскольку он был первым, опубликованным после того, как была установлена награда.

3. @cs95 Спасибо 🙂 благодаря щедрости у меня появилась возможность впервые взглянуть на Dask, так что спасибо.

Ответ №4:

Вот другой способ:

          dft.loc[dft.groupby(dft.index)['X'].apply(lambda x : x.notnull().sum() > 1)]


                X
           b    1.0
           b    1.0
           b    NaN
           c    1.0
           c    1.0
           c    1.0
 

Ответ №5:

Я новичок в Dask. У меня даже не установлен Dask на моем ноутбуке. Я прочитал документацию Dask и обнаружил, что Dask может выполнять reset_index() .

Если это разрешено, вот как подойти к проблеме.

Шаг 1:

 df1 = df.reset_index()
 

df1 даст вам:

 >>> df1
  index    X
0     a  1.0
1     a  NaN
2     a  NaN
3     b  1.0
4     b  1.0
5     b  NaN
6     c  1.0
7     c  1.0
8     c  1.0
 

Теперь у вас есть индекс и значение X.

Шаг 2:

Чтобы узнать, какое index значение имеет 2 или более нулей, вы можете сделать:

 df1.X.isnull().groupby([df1['index']]).sum().astype(int) < 2
 

Результатом этого будет:

 index
a    False
b     True
c     True
Name: X, dtype: bool
 

Шаг 3:

Теперь вы применяете это обратно к исходному df фрейму данных, и отфильтрованными записями будут записи с NaN меньше 2.

 df.loc[(df2.X.isnull().groupby([df2['index']]).sum().astype(int) < 2)]
 

Результатом этого будет:

      X
b  1.0
b  1.0
b  NaN
c  1.0
c  1.0
c  1.0
 

Я надеюсь, что Даск позволит вам сделать это. Если это произойдет, это будет способ получить результат.

Комментарии:

1. Очень смело с вашей стороны пытаться найти ответ для библиотеки, которую вы даже не установили

2. Установил Dask, опробовал его, и он работает. Это мило. Глядя на решение @anky, мое решение выглядит аналогично. Anky использует список индекса, чтобы получить аналогичный результат

3. В итоге я почти сделал свой код Dask похожим на Anky. Так что я бы выбрал код Анки

Ответ №6:

Вы можете использовать loc с серией логических значений:

 df.loc[df['X'].notna().groupby(level=0).sum().ge(2)]
 

На первом шаге мы получаем ряд для фильтрации:

 mask = df['X'].notna().groupby(level=0).sum().ge(2)
 

Результат:

 a    False
b     True
c     True
Name: X, dtype: bool
 

На втором шаге мы фильтруем с помощью loc :

 df.loc[mask]
 

Результат:

      X
b  1.0
b  1.0
b  NaN
c  1.0
c  1.0
c  1.0