Эффективный способ получить уникальные значения из 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?
Какова эквивалентность в пандах эффективного способа получения уникальных значений из 2 или более столбцов в pandas
?
6 ответов
У меня нет SFrame, но проверено на pd.DataFrame:
sf[["x", "y"]].stack().value_counts().index.tolist()
[2, 1, 8, 7, 6, 5, 4]
Посмотрите на этот ответ на аналогичный вопрос. Обратите внимание, что Панды pd.unique
Функция значительно быстрее, чем у Numpy.
>>> pd.unique(sf[['x','y']].values.ravel())
array([2, 8, 5, 4, 1, 7, 6], dtype=object)
SFrame
Я не использовал SFrame и не знаю, при каких условиях он копирует данные. (Делает выбор sf['x']
или же append
скопировать данные в память?). Есть pack_columns
а также stack
методы в SFrame, и если они не копируют данные, это должно работать:
sf[['x', 'y']].pack_columns(new_column_name='N').stack('N').unique()
панд
Если ваши данные помещаются в память, то вы, вероятно, можете сделать это эффективно в пандах без дополнительной копии.
# 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)
панды-х unique
более эффективен, чем Numpy's np.unique
потому что это не сортирует.
Самый простой способ, который я могу придумать, - это преобразовать в пустой массив и найти уникальные значения.
np.unique(sf[['x', 'y']].to_numpy())
array([1, 2, 4, 5, 6, 7, 8])
Если вам это нужно в кадре
SFrame({'xy_unique': np.unique(sf[['x', 'y']].to_numpy())})
Вот небольшой тест между тремя возможными методами:
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.
Хотя я не знаю, как это сделать в 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 столбца в строке в 1 строку столбца, сохраняя при этом доступ к ним в качестве словаря:
>>> 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.