значение столбца зависит от нескольких значений в других столбцах [duplicate]

Неявные объединения (что и называется вашим первым запросом) становятся намного более запутанными, трудными для чтения и сложными для поддержания, когда вам нужно начать добавлять в ваш запрос больше таблиц. Представьте, что вы делаете тот же запрос и тип соединения в четырех или пяти разных таблицах ... это кошмар.

Использование явного соединения (ваш второй пример) гораздо читабельнее и прост в обслуживании.

16
задан Dave Challis 5 August 2015 в 09:15
поделиться

6 ответов

7
ответ дан EdChum 24 August 2018 в 19:04
поделиться

Ответ JoeCondron (EDIT: до его последнего редактирования!) классный, но есть преимущество для значительного улучшения, избегая не-векторизации перечисления:

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

Улучшение является небольшим, если DataFrame относительно плоский:

In [4]: df = pd.DataFrame(np.random.choice([1, np.nan], (10000, 1500), p=(0.01, 0.99)))

In [5]: %timeit get_first_non_null(df)
10 loops, best of 3: 34.9 ms per loop

In [6]: %timeit get_first_non_null_vect(df)
10 loops, best of 3: 31.6 ms per loop

... но может иметь значение для slim DataFrames:

In [7]: df = pd.DataFrame(np.random.choice([1, np.nan], (10000, 15), p=(0.1, 0.9)))

In [8]: %timeit get_first_non_null(df)
100 loops, best of 3: 3.75 ms per loop

In [9]: %timeit get_first_non_null_vect(df)
1000 loops, best of 3: 718 µs per loop

По сравнению с векторизованной версией JoeCondron время выполнения очень (это все же немного быстрее для тонких DataFrames и немного медленнее для больших).

4
ответ дан Community 24 August 2018 в 19:04
поделиться

Я собираюсь взвесить здесь, поскольку я думаю, что это намного быстрее, чем любой из предлагаемых методов. argmin дает векторное значение первого значения False в каждой строке результата np.isnan, что является сложной частью. Он все еще полагается на цикл Python для извлечения значений, но поиск очень быстрый:

def get_first_non_null(df):
    a = df.values
    col_index = np.isnan(a).argmin(axis=1)
    return [a[row, col] for row, col in enumerate(col_index)]

EDIT: вот полностью векторизованное решение, которое может быть намного быстрее, в зависимости от формы вход. Обновленный бенчмаркинг ниже.

def get_first_non_null_vec(df):
    a = df.values
    n_rows, n_cols = a.shape
    col_index = np.isnan(a).argmin(axis=1)
    flat_index = n_cols * np.arange(n_rows) + col_index
    return a.ravel()[flat_index]

Если строка полностью равна нулю, то соответствующее значение также будет равно null. Вот несколько бенчмаркинга против решения unutbu:

df = pd.DataFrame(np.random.choice([1, np.nan], (10000, 1500), p=(0.01, 0.99)))
#%timeit df.stack().groupby(level=0).first().reindex(df.index)
%timeit get_first_non_null(df)
%timeit get_first_non_null_vec(df)
1 loops, best of 3: 220 ms per loop
100 loops, best of 3: 16.2 ms per loop
100 loops, best of 3: 12.6 ms per loop
In [109]:


df = pd.DataFrame(np.random.choice([1, np.nan], (100000, 150), p=(0.01, 0.99)))
#%timeit df.stack().groupby(level=0).first().reindex(df.index)
%timeit get_first_non_null(df)
%timeit get_first_non_null_vec(df)
1 loops, best of 3: 246 ms per loop
10 loops, best of 3: 48.2 ms per loop
100 loops, best of 3: 15.7 ms per loop


df = pd.DataFrame(np.random.choice([1, np.nan], (1000000, 15), p=(0.01, 0.99)))
%timeit df.stack().groupby(level=0).first().reindex(df.index)
%timeit get_first_non_null(df)
%timeit get_first_non_null_vec(df)
1 loops, best of 3: 326 ms per loop
1 loops, best of 3: 326 ms per loop
10 loops, best of 3: 35.7 ms per loop
21
ответ дан JoeCondron 24 August 2018 в 19:04
поделиться

groupby в axis=1

Если мы передаем вызываемый, который возвращает то же значение, мы группируем все столбцы вместе. Это позволяет нам использовать groupby.agg, который дает нам метод first, который делает этот простой

df.groupby(lambda x: 'Z', 1).first()

     Z
0  1.0
1  3.0
2  4.0
3  NaN

. Это возвращает фреймворк с именем столбца вещи, которую я возвращал в своем вызываемом


lookup, notna и idxmax

df.lookup(df.index, df.notna().idxmax(1))

array([ 1.,  3.,  4., nan])

argmin и нарезка

v = df.values
v[np.arange(len(df)), np.isnan(v).argmin(1)]

array([ 1.,  3.,  4., nan])
1
ответ дан piRSquared 24 August 2018 в 19:04
поделиться

Вот еще один способ сделать это:

In [183]: df.stack().groupby(level=0).first().reindex(df.index)
Out[183]: 
0     1
1     3
2     4
3   NaN
dtype: float64

Идея здесь заключается в использовании stack для перемещения столбцов в уровень индекса строки:

In [184]: df.stack()
Out[184]: 
0  A    1
   C    2
1  B    3
2  B    4
   C    5
dtype: float64

Теперь, если вы группируете по первому уровню строки, то есть исходному индексу, и принимаете первое значение из каждой группы, вы, по существу, получаете желаемый результат:

In [185]: df.stack().groupby(level=0).first()
Out[185]: 
0    1
1    3
2    4
dtype: float64

Все, что нам нужно для этого нужно повторно проиндексировать результат (используя исходный индекс), чтобы включить строки, которые полностью NaN:

df.stack().groupby(level=0).first().reindex(df.index)
9
ответ дан unutbu 24 August 2018 в 19:04
поделиться
2
ответ дан yangjie 24 August 2018 в 19:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: