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

1

Я хочу рассчитать среднее значение (т.е. Numpy.mean) и sem (т.е. scipy.stats.sem) уникальных значений float в каждой строке pandas DataFrame в векторном виде.

Пример ввода:

import pandas as pd
t = pd.DataFrame(data={'c1':[1.,2.,3.,4.],
                       'c2':[1.,2.,3.,3.],
                       'c3':[1.,2.,2.,2.],
                       'c4':[1.,1.,1.,1.]})
t.index.name = 'i'
# unique values: [1] [1,2] [1,2,3] [1,2,3,4]

Ожидаемый результат:

   mean       sem
i                
0   1.0       NaN
1   1.5  0.500000
2   2.0  0.577350
3   2.5  0.645497

Пожалуйста, не отправляйте не векторизованные решения вроде этого:

import numpy as np, scipy.stats as ss
def fun(x):
    r = x.transpose()[x.index[0]].value_counts(sort=False).rename('count')
    r.index.name = 'value'
    y = r.index.values
    return pd.DataFrame({'mean':np.mean(y), 'sem':ss.sem(y)}, index=[0])

t2 = t.groupby(t.index.names).apply(fun)
t2.index = t2.index.droplevel(1)

Реальный DataFrame имеет> 1e12 строк, поэтому неэффективные решения не будут делать.

В идеале было бы здорово иметь условие уникальности (т.е. абс. Разность или отклонение отношения от одного) от значений float, но любое эффективное решение было бы удивительным.

Спасибо за помощь!

  • 0
    Я не верю, что векторизованное решение возможно - в основном потому, что у панд нет векторизованной функции для определения уникальности.
  • 0
    @DYZ: у Панд есть такие функции. Например, pandas.Series.value_counts , pandas.Series.unique , pandas.core.groupby.SeriesGroupBy.unique , pandas.unique и т. Д. Вопрос заключается в том, существует ли способ эффективного объединения таких функций. Я полагаю, что решение транспонировало бы входной DataFrame таким образом, чтобы строки становились столбцами, а затем применяли одну из функций уникальности к столбцам Series. Обратите внимание, что настоящий DataFrame имеет MultiIndex, поэтому при его выполнении необходимо соблюдать осторожность.
Показать ещё 4 комментария
Теги:
pandas
mean
vectorization
rows

2 ответа

2
Лучший ответ

IIUC, pandas имеют sem, вам не нужно вызывать scipy

newdf=pd.DataFrame(list(map(set,t.values)))
newdf.T.agg(['mean','sem']).T
Out[436]: 
   mean       sem
0   1.0       NaN
1   1.5  0.500000
2   2.0  0.577350
3   2.5  0.645497
  • 0
    Хотя это работает правильно, это решение очень медленное. Его применение на 1000 строк занимает 2,8 секунды, что соответствует 88 годам для 1–12 строк.
  • 0
    @SV медленнее чем у тебя?
Показать ещё 6 комментариев
3

Вот почти векторизованное решение, единственная не-векторизованная операция создает вашу маску, которая в основном векторизована, но вам нужно создать один за столбец.

m = np.column_stack([t[col].duplicated() for col in t])
out = t.mask(m)
pd.DataFrame({'mean': np.mean(out), 'sem':ss.sem(out, nan_policy='omit').data})

    mean       sem
c1   2.5  0.645497
c2   2.0  0.577350
c3   1.5  0.500000
c4   1.0  0.000000

У меня недостаточно памяти, чтобы проверить это на вашем размере DataFrame, но вот образец на 1-миллионной строке DataFrame:

t = pd.concat([t]*250000)

In [649]: %%timeit
     ...: m = np.column_stack([t[col].duplicated() for col in t])
     ...: out = t.mask(m)
     ...: pd.DataFrame({'mean': np.mean(out), 'sem':ss.sem(out, nan_policy='omit').data})
     ...:
326 ms ± 10.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
  • 1
    Это круто.
  • 0
    Не могли бы вы проверить время моего решения?
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню