с питоном, выберите повторяющиеся элементы дольше, чем N

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

df = pd.DataFrame({'A':[1,1,2,3,3,3,3,3,4,4,4,4,4,4,4,5,5,5,5,6,6]}) df Out[1]: A 0 1 1 1 2 2 3 3 4 3 5 3 6 3 7 3 8 4 9 4 10 4 11 4 12 4 13 4 14 4 15 5 16 5 17 5 18 5 19 6 20 6 

Я пытаюсь отфильтровать числа, которые повторяются 4 раза и более, и вывод будет:

 df1 Out[2]: A 0 3 1 3 2 3 3 3 4 3 5 4 6 4 7 4 8 4 9 4 10 4 11 4 12 5 13 5 14 5 15 5 

Прямо сейчас я использую itemfreq для извлечения этой информации, это приводит к серии массивов, где тогда сложно сделать условие и фильтровать только эти числа. Я думаю, что должен быть другой самый простой способ сделать это. Некоторые идеи? Благодаря!

5 Solutions collect form web for “с питоном, выберите повторяющиеся элементы дольше, чем N”

groupby.filter, вероятно, самый простой способ:

 df.groupby('A').filter(lambda x: x.size > 3) Out: A 3 3 4 3 5 3 6 3 7 3 8 4 9 4 10 4 11 4 12 4 13 4 14 4 15 5 16 5 17 5 18 5 

numpy
В этом решении я отслеживаю, где значения не равны последующему значению. Затем, беря np.diff этих положений, дает мне длину каждой подпоследовательности.
Затем я могу проверить, если длина > 3 и используйте np.repeat с тем же списком длин, чтобы получить логический массив, необходимый для нарезки.

 def pir(d): v = dAvalues t = np.flatnonzero(v[1:] != v[:-1]) s = np.empty(t.size + 2, int) s[0] = -1 s[-1] = v.size - 1 s[1:-1] = t r = np.diff(s) return d[(r > 3).repeat(r)] pir(df) A 3 3 4 3 5 3 6 3 7 3 8 4 9 4 10 4 11 4 12 4 13 4 14 4 15 5 16 5 17 5 18 5 

numba
Используйте numba s JIT для запуска линейного алгоритма

 from numba import njit @njit def bslc(a, o, p): ref = a[0] count = 1 for i, x in enumerate(a[1:], 1): if x != ref: if count <= p: o[i-count:i] = False count = 1 ref = x else: count += 1 if count <= p: o[-count:] = False return o def pir2(d): v = dAvalues return d[bslc(v, np.ones(v.size, bool), 3)] 

тайминг

введите описание изображения здесь

 res.div(res.min(1), 0).T 10 30 100 300 1000 3000 10000 pir1 2.534343 2.139391 2.270708 2.252734 2.163466 2.197770 2.356690 pir1_5 1.000000 1.000000 1.000000 1.000000 1.003564 1.000000 1.000000 pir2 2.296362 2.042509 2.102017 1.986853 2.001767 2.051514 2.275013 div1 2.982864 2.491685 2.737808 2.448306 2.941023 3.038580 3.281304 div2 3.801531 3.416633 3.383586 3.054023 3.256094 3.913530 3.632997 div3 1.319752 1.009515 1.041559 1.031727 1.000000 1.066009 1.243461 ayhan 12.511852 12.910074 21.728097 21.594083 36.871183 63.998537 101.748270 wen 12.978138 15.125419 15.116332 12.846738 16.342696 16.241073 16.043509 roman 18.241104 21.198169 32.914250 37.305237 70.204290 106.672777 178.032963 roman_v_pir 10.178496 11.122943 12.522402 8.874233 11.489024 12.244398 11.689981 

КОД

функции

 def pir1(d): v = dAvalues t = np.flatnonzero(v[1:] != v[:-1]) s = np.empty(t.size + 2, int) s[0] = -1 s[-1] = v.size - 1 s[1:-1] = t r = np.diff(s) return d[(r > 3).repeat(r)] def pir1_5(d): v = dAvalues t = np.flatnonzero(v[1:] != v[:-1]) s = np.empty(t.size + 2, int) s[0] = -1 s[-1] = v.size - 1 s[1:-1] = t r = np.diff(s) return pd.DataFrame(v[(r > 3).repeat(r)]) @njit def bslc(a, o, p): ref = a[0] count = 1 for i, x in enumerate(a[1:], 1): if x != ref: if count <= p: o[i-count:i] = False count = 1 ref = x else: count += 1 if count <= p: o[-count:] = False return o def pir2(d): v = dAvalues return d[bslc(v, np.ones(v.size, bool), 3)] def div1(d): a = dAvalues unq, c = np.unique(a, return_counts=1) return d[np.in1d(a, unq[c >= 4])] def div2(df): return df[df.A.isin(np.flatnonzero(np.bincount(df.A) >= 4))] def div3(df, N=4): a = df.A.values mask = np.concatenate(( [True], a[1:] != a[:-1], [True] )) idx = np.flatnonzero(mask) count = idx[1:] - idx[:-1] valid_mask = count>=N good_idx = idx[:-1][valid_mask] out_arr = np.repeat(a[good_idx], count[valid_mask]) return pd.DataFrame(out_arr) ayhan = lambda df: df.groupby('A').filter(lambda x: x.size > 3) wen = lambda df: df[df.A.isin(df.A.value_counts()[df.A.value_counts()>=4].index)] roman = lambda df: df[df.groupby('A').A.transform(len)>3] roman_v_pir = lambda df: df[df.groupby('A').A.transform('size')>3] 

тестирование

 res = pd.DataFrame( index=[10, 30, 100, 300, 1000, 3000, 10000], columns='pir1 pir1_5 pir2 div1 div2 div3 ayhan wen roman roman_v_pir'.split(), dtype=float ) for i in res.index: n = int(i ** .5) diffs = np.random.randint(100, size=n) d = pd.DataFrame(dict(A=np.arange(n).repeat(diffs))) for j in res.columns: stmt = '{}(d)'.format(j) setp = 'from __main__ import d, {}'.format(j) res.at[i, j] = timeit(stmt, setp, number=10) 

Подход №1: Один из способов NumPy –

 a = df.A.values unq, c = np.unique(a, return_counts=1) df_out = df[np.in1d(a,unq[c>=4])] 

Подход №2: NumPy + Pandas смешивают однострочный набор для положительных чисел –

 df[df.A.isin(np.flatnonzero(np.bincount(df.A)>=4))] 

Подход № 3: Используя тот факт, что блок данных отсортирован по соответствующему столбцу, вот один более глубокий подход NumPy –

 def filter_df(df, N=4): a = df.A.values mask = np.concatenate(( [True], a[1:] != a[:-1], [True] )) idx = np.flatnonzero(mask) count = idx[1:] - idx[:-1] valid_mask = count>=N good_idx = idx[:-1][valid_mask] out_arr = np.repeat(a[good_idx], count[valid_mask]) out_df = pd.DataFrame(out_arr) return out_df 

Бенчмаркинг

@piRSquared рассмотрел обширный бенчмаркинг для всех подходов, опубликованных до сих пор, и, как кажется, pir1_5 и div3 видимому, являются top-2 . Но тайминги кажутся сопоставимыми, и это способствовало мне более пристальному взгляду. В этом бенчмаркинге у нас было timeit(stmt, setp, number=10) , в котором используется постоянное количество итераций для запуска timeit , что не является самым надежным методом для синхронизации, особенно для небольших наборов данных. Кроме того, наборы данных казались небольшими, поскольку тайминги для самого большого набора данных были в micro-sec . Итак, чтобы смягчить эти два вопроса – я предлагаю использовать %timeit IPython, который автоматически вычисляет оптимальное количество итераций для timeit для запуска, т. Е. Большее количество итераций для меньших наборов данных, чем более крупные. Это должно быть более надежным. Кроме того, я предлагаю включить более крупные наборы данных, чтобы тайминги переходили в milli-sec и sec . Таким образом, с этими двумя изменениями новая настройка бенчмаркинга выглядела примерно так (не забудьте скопировать и вставить на консоль IPython для ее запуска) –

 sizes =[10, 30, 100, 300, 1000, 3000, 10000, 100000, 1000000, 10000000] timings = np.zeros((len(sizes),2)) for i,s in enumerate(sizes): diffs = np.random.randint(100, size=s) d = pd.DataFrame(dict(A=np.arange(s).repeat(diffs))) res = %timeit -oq div3(d) timings[i,0] = res.best res = %timeit -oq pir1_5(d) timings[i,1] = res.best timings_df = pd.DataFrame(timings, columns=(('div3(sec)','pir1_5(sec)'))) timings_df.index = sizes timings_df.index.name = 'Datasizes' 

Для полноты, подходы были –

 def pir1_5(d): v = dAvalues t = np.flatnonzero(v[1:] != v[:-1]) s = np.empty(t.size + 2, int) s[0] = -1 s[-1] = v.size - 1 s[1:-1] = t r = np.diff(s) return pd.DataFrame(v[(r > 3).repeat(r)]) def div3(df, N=4): a = df.A.values mask = np.concatenate(( [True], a[1:] != a[:-1], [True] )) idx = np.flatnonzero(mask) count = idx[1:] - idx[:-1] valid_mask = count>=N good_idx = idx[:-1][valid_mask] out_arr = np.repeat(a[good_idx], count[valid_mask]) return pd.DataFrame(out_arr) 

Настройка синхронизации была запущена на консоли IPython (поскольку мы используем магические функции). Результаты выглядели так:

 In [265]: timings_df Out[265]: div3(sec) pir1_5(sec) Datasizes 10 0.000090 0.000089 30 0.000096 0.000097 100 0.000109 0.000118 300 0.000157 0.000182 1000 0.000303 0.000396 3000 0.000713 0.000998 10000 0.002252 0.003701 100000 0.023257 0.036480 1000000 0.258133 0.398812 10000000 2.603467 3.759063 

Таким образом, цифры ускорения с div3 над pir1_5 :

 In [266]: timings_df.iloc[:,1]/timings_df.iloc[:,0] Out[266]: Datasizes 10 0.997704 30 1.016446 100 1.077129 300 1.163333 1000 1.304689 3000 1.400464 10000 1.643474 100000 1.568554 1000000 1.544987 10000000 1.443868 

Просто value_counts

  df[df.A.isin(df.A.value_counts()[df.A.value_counts()>=4].index)] Out[632]: A 3 3 4 3 5 3 6 3 7 3 8 4 9 4 10 4 11 4 12 4 13 4 14 4 15 5 16 5 17 5 18 5 

Сроки:

 %timeit df.groupby('A').filter(lambda x: x.size > 3) 1000 loops, best of 3: 1.25 ms per loop %timeit df[df.groupby('A').A.transform(len)>3] 1000 loops, best of 3: 2.05 ms per loop %timeit df[df.A.isin(np.flatnonzero(np.bincount(df.A)>=4))] 1000 loops, best of 3: 283 µs per loop %timeit df[df.A.isin(df.A.value_counts()[df.A.value_counts()>=4].index)] 1000 loops, best of 3: 1.29 ms per loop %timeit pir(df) 1000 loops, best of 3: 178 µs per loop 

@ Ответ Дивакара является самым быстрым, прямо сейчас это ответ Пира.


EDIT: Извините, ребята, потому что я дома, я снова переделываю все сроки

 %timeit df.groupby('A').filter(lambda x: x.size > 3)#ayhan 1000 loops, best of 3: 2.41 ms per loop %timeit df[df.groupby('A').A.transform(len)>3]#Roman 1000 loops, best of 3: 1.62 ms per loop %timeit df[df.A.isin(np.flatnonzero(np.bincount(df.A)>=4))]#Diva1 The slowest run took 135.00 times longer than the fastest. This could mean that an intermediate result is being cached. 1000 loops, best of 3: 348 µs per loop %timeit df[df.A.isin(df.A.value_counts()[df.A.value_counts()>=4].index)]#Wen 1000 loops, best of 3: 1.62 ms per loop %timeit pir(df)#PiR 1000 loops, best of 3: 254 µs per loop %timeit Diva(df)#Diva2 1000 loops, best of 3: 125 µs per loop 

С pandas.Dataframe.tranform() :

 print(df[df.groupby('A').A.transform(len)>3]) 

Выход:

  A 3 3 4 3 5 3 6 3 7 3 8 4 9 4 10 4 11 4 12 4 13 4 14 4 15 5 16 5 17 5 18 5 
  • разброс графиков со строковыми массивами в matplotlib
  • Как я могу удалить дубликаты данных в одном столбце, групповом в пандах?
  • Pandas dataframe скрывает функциональность индекса?
  • Pandas DataFrame нарезка днем ​​/ часом / минутой
  • заменить строку / значение на весь фрейм данных
  • как построить время по оси Y в формате «% H:% M» в matplotlib?
  • сделать двумерную матрицу строк для соответствия координатам сетки
  • Создайте индексированное datetime из информации о дате / времени в 3 столбцах, используя pandas
  • Панда-бар с конкретными цветами и расположением легенд?
  • Загрузка файла с несколькими строками JSON в Pandas Pandas
  • Pandas: .groupby (). Size () и проценты
  • Python - лучший язык программирования в мире.