Функция R expand.grid() в Python
Есть ли функция Python, аналогичная функции expand.grid() в R? Заранее спасибо.
(РЕДАКТИРОВАТЬ) Ниже приведено описание этой функции R и пример.
Create a Data Frame from All Combinations of Factors
Description:
Create a data frame from all combinations of the supplied vectors
or factors.
> x <- 1:3
> y <- 1:3
> expand.grid(x,y)
Var1 Var2
1 1 1
2 2 1
3 3 1
4 1 2
5 2 2
6 3 2
7 1 3
8 2 3
9 3 3
(EDIT2) Ниже приведен пример с пакетом rpy. Я хотел бы получить тот же объект вывода, но без использования R:
>>> from rpy import *
>>> a = [1,2,3]
>>> b = [5,7,9]
>>> r.assign("a",a)
[1, 2, 3]
>>> r.assign("b",b)
[5, 7, 9]
>>> r("expand.grid(a,b)")
{'Var1': [1, 2, 3, 1, 2, 3, 1, 2, 3], 'Var2': [5, 5, 5, 7, 7, 7, 9, 9, 9]}
РЕДАКТИРОВАТЬ 02/09/2012: я действительно потерял с Python. Код Льва Левицкого, приведенный в его ответе, не работает для меня:
>>> a = [1,2,3]
>>> b = [5,7,9]
>>> expandgrid(a, b)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in expandgrid
NameError: global name 'itertools' is not defined
Однако модуль itertools, кажется, установлен (набрав from itertools import *
не возвращает никакого сообщения об ошибке)
11 ответов
Вот пример, который дает вывод, аналогичный тому, что вам нужно:
import itertools
def expandgrid(*itrs):
product = list(itertools.product(*itrs))
return {'Var{}'.format(i+1):[x[i] for x in product] for i in range(len(itrs))}
>>> a = [1,2,3]
>>> b = [5,7,9]
>>> expandgrid(a, b)
{'Var1': [1, 1, 1, 2, 2, 2, 3, 3, 3], 'Var2': [5, 7, 9, 5, 7, 9, 5, 7, 9]}
Разница связана с тем, что в itertools.product
самый правый элемент продвигается на каждой итерации. Вы можете настроить функцию, отсортировав product
список умно, если это важно.
Просто используйте списочные представления:
>>> [(x, y) for x in range(5) for y in range(5)]
[(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (3, 0), (3, 1), (3, 2), (3, 3), (3, 4), (4, 0), (4, 1), (4, 2), (4, 3), (4, 4)]
при желании преобразовать в массив numy:
>>> import numpy as np
>>> x = np.array([(x, y) for x in range(5) for y in range(5)])
>>> x.shape
(25, 2)
Я проверил до 10000 x 10000, и производительность python сравнима с таковой для expand.grid в R. Использование кортежа (x, y) примерно на 40% быстрее, чем использование списка [x, y] в понимании.
ИЛИ ЖЕ...Примерно в 3 раза быстрее с np.meshgrid и гораздо менее интенсивным использованием памяти.
%timeit np.array(np.meshgrid(range(10000), range(10000))).reshape(2, 100000000).T
1 loops, best of 3: 736 ms per loop
в R:
> system.time(expand.grid(1:10000, 1:10000))
user system elapsed
1.991 0.416 2.424
Имейте в виду, что R имеет массивы на основе 1, а Python - на основе 0.
product
от itertools
это ключ к вашему решению. Он производит декартово произведение входов.
from itertools import product
def expand_grid(dictionary):
return pd.DataFrame([row for row in product(*dictionary.values())],
columns=dictionary.keys())
dictionary = {'color': ['red', 'green', 'blue'],
'vehicle': ['car', 'van', 'truck'],
'cylinders': [6, 8]}
>>> expand_grid(dictionary)
color cylinders vehicle
0 red 6 car
1 red 6 van
2 red 6 truck
3 red 8 car
4 red 8 van
5 red 8 truck
6 green 6 car
7 green 6 van
8 green 6 truck
9 green 8 car
10 green 8 van
11 green 8 truck
12 blue 6 car
13 blue 6 van
14 blue 6 truck
15 blue 8 car
16 blue 8 van
17 blue 8 truck
Документация панд определяет expand_grid
функция:
def expand_grid(data_dict):
"""Create a dataframe from every combination of given values."""
rows = itertools.product(*data_dict.values())
return pd.DataFrame.from_records(rows, columns=data_dict.keys())
Чтобы этот код работал, вам понадобятся следующие два импорта:
import itertools
import pandas as pd
Выход является pandas.DataFrame
который является наиболее сопоставимым объектом в Python с R data.frame
,
Некоторое время я размышлял над этим, и я не был удовлетворен предложенными решениями, поэтому я нашел свое собственное, которое значительно проще (но, вероятно, медленнее). Функция использует numpy.meshgrid для создания сетки, затем выравнивает сетки в одномерные массивы и собирает их вместе:
def expand_grid(x, y):
xG, yG = np.meshgrid(x, y) # create the actual grid
xG = xG.flatten() # make the grid 1d
yG = yG.flatten() # same
return pd.DataFrame({'x':xG, 'y':yG}) # return a dataframe
Например:
import numpy as np
import pandas as pd
p, q = np.linspace(1, 10, 10), np.linspace(1, 10, 10)
def expand_grid(x, y):
xG, yG = np.meshgrid(x, y) # create the actual grid
xG = xG.flatten() # make the grid 1d
yG = yG.flatten() # same
return pd.DataFrame({'x':xG, 'y':yG})
print expand_grid(p, q).head(n = 20)
Я знаю, что это старый пост, но я решил поделиться своей простой версией!
Из приведенных выше решений я сделал это
import itertools
import pandas as pd
a = [1,2,3]
b = [4,5,6]
ab = list(itertools.product(a,b))
abdf = pd.DataFrame(ab,columns=("a","b"))
и следующий результат
a b
0 1 4
1 1 5
2 1 6
3 2 4
4 2 5
5 2 6
6 3 4
7 3 5
8 3 6
Функция ParameterGrid от Scikit делает то же самое, что и expand_grid(из R). Пример:
from sklearn.model_selection import ParameterGrid
grid = [{'kernel': ['linear']}, {'kernel': ['rbf'], 'gamma': [1, 10]}]
Вы можете получить доступ к контенту, преобразовав его в список:
list(ParameterGrid(grid))
выход:[{'kernel': 'linear'},
{'gamma': 1, 'kernel': 'rbf'},
{'gamma': 10, 'kernel': 'rbf'}]
Доступ к элементам по индексу
list(ParameterGrid(grid))[1]
вы получите что-то вроде этого:
{'gamma': 1, 'kernel': 'rbf'}
Вот еще одна версия, которая возвращает pandas.DataFrame:
import itertools as it
import pandas as pd
def expand_grid(*args, **kwargs):
columns = []
lst = []
if args:
columns += xrange(len(args))
lst += args
if kwargs:
columns += kwargs.iterkeys()
lst += kwargs.itervalues()
return pd.DataFrame(list(it.product(*lst)), columns=columns)
print expand_grid([0,1], [1,2,3])
print expand_grid(a=[0,1], b=[1,2,3])
print expand_grid([0,1], b=[1,2,3])
пижанитор _expand_grid()
возможно, самое естественное решение, особенно если вы родом из R.
Использование заключается в том, что вы устанавливаете
others
аргумент в словарь. Элементы в словаре могут иметь разную длину и тип. Возвращаемое значение — это кадр данных pandas.
import janitor as jn
jn.expand_grid(others = {
'x': range(0, 4),
'y': ['a', 'b', 'c'],
'z': [False, True]
})
Вот решение для произвольного количества разнородных типов столбцов. Это основано на
numpy.meshgrid
. Ответ Томаса Брауна работает для однородных типов столбцов. Ответ Нейта работает для двух столбцов.
import pandas as pd
import numpy as np
def expand_grid(*xi, columns=None):
"""Expand 1-D arrays xi into a pd.DataFrame
where each row is a unique combination of the xi.
Args:
x1, ..., xn (array_like): 1D-arrays to expand.
columns (list, optional): Column names for the output
DataFrame.
Returns:
Given vectors `x1, ..., xn` with lengths `Ni = len(xi)`
a pd.DataFrame of shape (prod(Ni), n) where rows are:
x1[0], x2[0], ..., xn-1[0], xn[0]
x1[1], x2[0], ..., xn-1[0], xn[0]
...
x1[N1 -1], x2[0], ..., xn-1[0], xn[0]
x1[0], x2[1], ..., xn-1[0], xn[0]
x1[1], x2[1], ..., xn-1[0], xn[0]
...
x1[N1 - 1], x2[N2 - 1], ..., xn-1[Nn-1 - 1], xn[Nn - 1]
"""
if columns is None:
columns = pd.RangeIndex(0, len(xi))
elif columns is not None and len(columns) != len(xi):
raise ValueError(
" ".join(["Expecting", str(len(xi)), "columns but",
str(len(columns)), "provided instead."])
)
return pd.DataFrame({
coln: arr.flatten() for coln, arr in zip(columns, np.meshgrid(*xi))
})
Ты пытался product
от itertools
? На мой взгляд, немного проще в использовании, чем некоторые из этих методов (за исключением pandas
а также meshgrid
). Имейте в виду, что эта установка фактически вытягивает все элементы из итератора в список, а затем преобразует его в ndarray
так что будьте осторожны с большими размерами или удалите np.asarray(list(combs))
для сеток более высокой размерности, если вы не хотите исчерпать память, вы можете обратиться к итератору для конкретных комбинаций. я очень рекомендую meshgrid
для этого, хотя:
#Generate square grid from axis
from itertools import product
import numpy as np
a=np.array(list(range(3)))+1 # axis with offset for 0 base index to 1
points=product(a,repeat=2) #only allow repeats for (i,j), (j,i) pairs with i!=j
np.asarray(list(points)) #convert to ndarray
И я получаю следующий вывод из этого:
array([[1, 1],
[1, 2],
[1, 3],
[2, 1],
[2, 2],
[2, 3],
[3, 1],
[3, 2],
[3, 3]])