2016-11-06 2 views
3

Я хочу выбрать столбцы из DataFrame в соответствии с определенным условием. Я знаю, что это можно сделать с помощью цикла, но мой df очень велик, поэтому эффективность имеет решающее значение. Условие выбора столбца имеет либо только не-нанные записи, либо последовательность только nans, за которыми следует последовательность только не-nan записей.Условный выбор столбцов в pandas

Вот пример. Рассмотрим следующий DataFrame:

pd.DataFrame([[1, np.nan, 2, np.nan], [2, np.nan, 5, np.nan], [4, 8, np.nan, 1], [3, 2, np.nan, 2], [3, 2, 5, np.nan]]) 

    0 1 2 3 
0 1 NaN 2.0 NaN 
1 2 NaN 5.0 NaN 
2 4 8.0 NaN 1.0 
3 3 2.0 NaN 2.0 
4 3 2.0 5.0 NaN 

От него, я хотел бы, чтобы выбрать столбцы только 0 и 1. Любые советы о том, как сделать это эффективно без зацикливания?

ответ

2

логика

  • считать нули в каждом столбце. если единственные нули в начале, то число нулей в столбце должно быть равно позиции первого действительного индекса.
  • получить первый действительный индекс
  • отрезать индекс нулевым числом и сравнить с первыми действительными индексами. Если они равны, то это хороший столбец

cnull = df.isnull().sum() 
fvald = df.apply(pd.Series.first_valid_index) 
cols = df.index[cnull] == fvald 
df.loc[:, cols] 

enter image description here


отредактированный с помощью улучшения скорости

старых ответов

def pir1(df): 
    cnull = df.isnull().sum() 
    fvald = df.apply(pd.Series.first_valid_index) 
    cols = df.index[cnull] == fvald 
    return df.loc[:, cols] 

гораздо быстрее ответ, используя тот же логический

def pir2(df): 
    nulls = np.isnan(df.values) 
    null_count = nulls.sum(0) 
    first_valid = nulls.argmin(0) 
    null_on_top = null_count == first_valid 
    filtered_data = df.values[:, null_on_top] 
    filtered_columns = df.columns.values[null_on_top] 
    return pd.DataFrame(filtered_data, df.index, filtered_columns) 

enter image description here

+0

Благодаря @piRSquared. Это решение действительно выполняет свою работу, но для запуска требуется более 3-х раз дольше, чем решение, размещенное ниже – splinter

+0

@splinter. Я не удивлен. Я думал о том, чтобы идти по маршруту, который взял Никил, но я выбрал краткость. Никиль дал хороший ответ. Я обновляю свой пост, используя ту же логику, но используя несколько трюков, чтобы ускорить его. – piRSquared

+0

Звучит отлично @piRSquared – splinter

1

DF Рассмотрим, как показано, который имеет Nans в различных возможных местах:

Image

1.Обе стороны Nans настоящее:

Создать маску, заменив все пренебрежимо малых с 0 и конечными значениями с 1-х:

mask = np.where(np.isnan(df), 0, 1) 

Возьмите это соответствующая разница элемент по каждому колонка. Затем возьмите модуль его значений. Логика здесь заключается в том, что всякий раз, когда в каждом столбце есть три уникальных значения, отбросьте этот столбец (а именно → -1,1,0), так как будет разрыв в последовательности для такой ситуации.

Идея заключается в том, чтобы взять сумму и создать подмножество везде, где сумма будет иметь значение меньше 2. (Так как после принятия мода мы получаем 1,1,0). Итак, в крайнем случае мы получаем сумму как 2, и эти столбцы, конечно, не пересекаются и должны быть отброшены.

criteria = pd.DataFrame(mask, columns=df.columns).diff(1).abs().sum().lt(2) 

Наконец транспонирования DF и использовать это условие и повторно транспонирование, чтобы получить желаемый результат, имеющий только Nans в виде одной порции, и конечные значения в другом.

df.loc[:, criteria] 

Image

2. Nans присутствует на вершине:

mask = np.where(np.isnan(df), 0, 1) 
criteria = pd.DataFrame(mask, columns=df.columns).diff(1).ne(-1).any() 
df.loc[:, criteria] 

Image

+0

Отлично работает @NickiMaveli, и он делает это в 3 раза быстрее, чем решение выше. – splinter

Смежные вопросы