Функция 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]])
Другие вопросы по тегам