Эффективный способ получения уникальных значений из 2 или более столбцов в Dataframe

Учитывая матрицу от SFrame:

>>> from sframe import SFrame
>>> sf =SFrame({'x':[1,1,2,5,7], 'y':[2,4,6,8,2], 'z':[2,5,8,6,2]})
>>> sf
Columns:
    x   int
    y   int
    z   int

Rows: 5

Data:
+---+---+---+
| x | y | z |
+---+---+---+
| 1 | 2 | 2 |
| 1 | 4 | 5 |
| 2 | 6 | 8 |
| 5 | 8 | 6 |
| 7 | 2 | 2 |
+---+---+---+
[5 rows x 3 columns]

Я хочу получить уникальные значения для столбцов x и y, и я могу сделать это как таковое:

>>> sf['x'].unique().append(sf['y'].unique()).unique()
dtype: int
Rows: 7
[2, 8, 5, 4, 1, 7, 6]

Таким образом, я получаю уникальные значения x и уникальные значения y, затем добавляю их и получаю уникальные значения добавленного списка.

Я мог бы также сделать это как таковой:

>>> sf['x'].append(sf['y']).unique()
dtype: int
Rows: 7
[2, 8, 5, 4, 1, 7, 6]

Но так, если мои столбцы x и y огромны с большим количеством дубликатов, я бы добавил его в очень огромный контейнер, прежде чем получить уникальный.

Есть ли более эффективный способ получить уникальные значения комбинированных столбцов, созданных из 2 или более столбцов в SFrame?

Какова эквивалентность в pandas эффективного способа получения уникальных значений из 2 или более столбцов в pandas?

Ответ 1

Самый простой способ, который я могу придумать, - это преобразовать в массив numpy, затем найти уникальные значения

np.unique(sf[['x', 'y']].to_numpy())

array([1, 2, 4, 5, 6, 7, 8])

Если вам это нужно в sframe

SFrame({'xy_unique': np.unique(sf[['x', 'y']].to_numpy())})

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

Ответ 2

У меня нет SFrame, но тестируется на pd.DataFrame:

  sf[["x", "y"]].stack().value_counts().index.tolist()
  [2, 1, 8, 7, 6, 5, 4]

Ответ 3

SFrame

Я не использовал SFrame и не знаю, на каких условиях он копирует данные. (Делает ли выбор sf['x'] или append копирование данных в память?). В SFrame существуют методы pack_columns и stack, и если они не копируют данные, это должно работать:

sf[['x', 'y']].pack_columns(new_column_name='N').stack('N').unique()

pandas

Если ваши данные вписываются в память, вы можете сделать это в pandas эффективно без дополнительной копии.

# copies the data to memory
df = sf[['x', 'y']].to_dataframe()

# a reference to the underlying numpy array (no copy)
vals = df.values

# 1d array: 
# (numpy.ravel doesn't copy if it doesn't have to - it depends on the data layout)
if np.isfortran(vals):
    vals_1d = vals.ravel(order='F')
else:
    vals_1d = vals.ravel(order='C')

uniques = pd.unique(vals_1d)

pandas unique более эффективен, чем numpy np.unique, потому что он не сортирует.

Ответ 4

Посмотрите этот ответ на аналогичный вопрос. Обратите внимание, что функция Pandas 'pd.unique значительно быстрее, чем у Numpy.

>>> pd.unique(sf[['x','y']].values.ravel())
array([2, 8, 5, 4, 1, 7, 6], dtype=object)

Ответ 5

Хотя я не знаю, как это сделать в SFrame, здесь более длинное объяснение ответа @Merlin:

>>> import pandas as pd
>>> df = pd.DataFrame({'x':[1,1,2,5,7], 'y':[2,4,6,8,2], 'z':[2,5,8,6,2]})
>>> df[['x', 'y']]
   x  y
0  1  2
1  1  4
2  2  6
3  5  8
4  7  2

Чтобы извлечь только столбцы X и Y

>>> df[['x', 'y']] # Extract only columns x and y
   x  y
0  1  2
1  1  4
2  2  6
3  5  8
4  7  2

Чтобы уложить 2 столбца на строку в одну строку столбца, сохраняя при этом доступ к ним в качестве словаря:

>>> df[['x', 'y']].stack()                       
0  x    1
   y    2
1  x    1
   y    4
2  x    2
   y    6
3  x    5
   y    8
4  x    7
   y    2
dtype: int64
>>> df[['x', 'y']].stack()[0]      
x    1
y    2
dtype: int64
>>> df[['x', 'y']].stack()[0]['x']
1
>>> df[['x', 'y']].stack()[0]['y']
2

Подсчитайте отдельные значения всех элементов в комбинированных столбцах:

>>> df[['x', 'y']].stack().value_counts() # index(i.e. keys)=elements, Value=counts
2    3
1    2
8    1
7    1
6    1
5    1
4    1

Для доступа к индексу и подсчетам:

>>> df[['x', 'y']].stack().value_counts().index      
Int64Index([2, 1, 8, 7, 6, 5, 4], dtype='int64')
>>> df[['x', 'y']].stack().value_counts().values  
array([3, 2, 1, 1, 1, 1, 1])

Преобразовать в список:

>>> sf[["x", "y"]].stack().value_counts().index.tolist()
[2, 1, 8, 7, 6, 5, 4]

Все-таки ответ SFrame тоже будет замечательным. Тот же синтаксис не работает для SFrame.

Ответ 6

Вот небольшой тест трех возможных методов:

from sframe import SFrame
import numpy as np
import pandas as pd
import timeit

sf = SFrame({'x': [1, 1, 2, 5, 7], 'y': [2, 4, 6, 8, 2], 'z': [2, 5, 8, 6, 2]})


def f1(sf):
    return sf['x'].unique().append(sf['y'].unique()).unique()


def f2(sf):
    return sf['x'].append(sf['y']).unique()


def f3(sf):
    return np.unique(sf[['x', 'y']].to_numpy())

N = 1000

print timeit.timeit('f1(sf)', setup='from __main__ import f1, sf', number=N)
print timeit.timeit('f2(sf)', setup='from __main__ import f2, sf', number=N)
print timeit.timeit('f3(sf)', setup='from __main__ import f3, sf', number=N)

# 13.3195129933
# 4.66225642657
# 3.65669089489
# [Finished in 23.6s]

Тест с использованием python2.7.11 x64 на windows7 + i7_2.6ghz

Заключение: предлагаю вам использовать np.unique, в основном f3.