Как мне найти: является ли первое не-NaN значение в каждом столбце максимальным для этого столбца в DataFrame? - PullRequest
0 голосов
/ 16 сентября 2018

Например:

      0     1
0  87.0   NaN
1   NaN  99.0
2   NaN   NaN
3   NaN   NaN
4   NaN  66.0
5   NaN   NaN
6   NaN  77.0
7   NaN   NaN
8   NaN   NaN
9  88.0   NaN

Мой ожидаемый результат: [False, True], поскольку 87 - первое значение! NaN, но не максимальное значение в столбце 0.99 однако является первым значением! NaN и действительно является максимумом в этом столбце.

Ответы [ 5 ]

0 голосов
/ 16 сентября 2018

Мы можем использовать numpy nanmax здесь для эффективного решения:

a = df.values
np.nanmax(a, 0) == a[np.isnan(a).argmin(0), np.arange(a.shape[1])]

array([False,  True])

Сроки (Здесь представлено множество вариантов):


Функция

def chris(df):
    a = df.values
    return np.nanmax(a, 0) == a[np.isnan(a).argmin(0), np.arange(a.shape[1])]

def bradsolomon(df):
    df.values[df.notnull().idxmax(), np.arange(df.shape[1])] == df.max(axis=0).values

def wen1(df):
    return df.groupby([1]*len(df)).first()==df.max()

def wen2(df):
    return df.bfill().iloc[0]==df.max()

def wen3(df):
    return df.idxmax()==df.apply(pd.Series.first_valid_index)

def rafaelc(df):
    return np.isnan(df.values).argmin(axis=0) == df.fillna(-np.inf).values.argmax(axis=0)

def pir(df):
    return df.notna().idxmax() == df.idxmax()

Настройка

res = pd.DataFrame(
       index=['chris', 'bradsolomon', 'wen1', 'wen2', 'wen3', 'rafaelc', 'pir'],
       columns=[10, 20, 30, 100, 500, 1000],
       dtype=float
)

for f in res.index:
    for c in res.columns:
        a = np.random.rand(c, c)
        a[a > 0.4] = np.nan
        df = pd.DataFrame(a)
        stmt = '{}(df)'.format(f)
        setp = 'from __main__ import df, {}'.format(f)
        res.at[f, c] = timeit(stmt, setp, number=50)

ax = res.div(res.min()).T.plot(loglog=True)
ax.set_xlabel("N");
ax.set_ylabel("time (relative)");

plt.show()

Результаты

enter image description here

0 голосов
/ 16 сентября 2018

Использование чистого numpy (я думаю, что это очень быстро)

>>> np.isnan(df.values).argmin(axis=0) == df.fillna(-np.inf).values.argmax(axis=0)
array([False,  True])

Идея состоит в том, чтобы сравнить, является ли индекс первого non-nan индексом argmax.

Задержка

df = pd.concat([df]*1000).reset_index(drop=True) # setup

%timeit np.isnan(df.values).argmin(axis=0) == df.fillna(-np.inf).values.argmax(axis=0)
207 µs ± 8.83 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit df.groupby([1]*len(df)).first()==df.max()
9.78 ms ± 339 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit df.bfill().iloc[0]==df.max()
824 µs ± 47.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit df.stack().reset_index(level=1).drop_duplicates('level_1').set_index('level_1')[0]==df.max()
3.55 ms ± 249 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit df.idxmax()==df.apply(pd.Series.first_valid_index)
1.5 ms ± 25 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit df.values[df.notnull().idxmax(), np.arange(df.shape[1])] == df.max(axis=0)
1.13 ms ± 14.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit df.values[(~np.isnan(df.values)).argmax(axis=0), np.arange(df.shape[1])] == df.max(axis=0).values
450 µs ± 20.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
0 голосов
/ 16 сентября 2018

После публикации вопроса я придумал:

def nice_method_name_here(sr):
    return sr[sr > 0][0] == np.max(sr)

print(df.apply(nice_method_name_here))

, который, кажется, работает, но еще не уверен!

0 голосов
/ 16 сентября 2018

Вы можете сделать что-то похожее на ответ Венса с базовыми массивами Numpy:

>>> df.values[df.notnull().idxmax(), np.arange(df.shape[1])] == df.max(axis=0).values
array([False,  True])

df.max(axis=0) дает максимальное значение по столбцам

Левые индексы df.values, который является 2d массивом, чтобы сделать его 1d массивом и сравнить его поэлементно с максимумами на столбец.

Если вы исключите .values из правой части, результат будет простоСерия Панд:

>>> df.values[df.notnull().idxmax(), np.arange(df.shape[1])] == df.max(axis=0)
0    False
1     True
dtype: bool
0 голосов
/ 16 сентября 2018

Вариант а) : Просто выполните groupby с first

(не может быть 100% надежно )

df.groupby([1]*len(df)).first()==df.max()
Out[89]: 
       0     1
1  False  True

Вариант b) : bfill

Или с использованием bfill (Заполните любое значение NaN обратным значением в столбце, затем первая строка после bfill будет первой неNaN значение)

df.bfill().iloc[0]==df.max()
Out[94]: 
0    False
1     True
dtype: bool

Опция c) : stack

df.stack().reset_index(level=1).drop_duplicates('level_1').set_index('level_1')[0]==df.max()
Out[102]: 
level_1
0    False
1     True
dtype: bool

Опция d) : idxmax сfirst_valid_index

df.idxmax()==df.apply(pd.Series.first_valid_index)
Out[105]: 
0    False
1     True
dtype: bool

Опция e) (из Пир) : idxmax с isna

df.notna().idxmax() == df.idxmax()     
Out[107]: 
0    False
1     True
dtype: bool
...