Применить функцию к перекрестному произведению pandas строка-строка - PullRequest
12 голосов
/ 03 августа 2020

У меня есть два pandas DataFrames / Series, содержащие по одной строке каждая.

df1 = pd.DataFrame([1, 2, 3, 4])
df2 = pd.DataFrame(['one', 'two', 'three', 'four'])

Теперь я хочу получить все возможные комбинации в матрицу / DataFrame n * n со значениями для всех перекрестных продуктов, вывод пользовательской функции.

def my_function(x, y):
    return f"{x}:{y}"

Следовательно, это должно привести к:

df = pd.DataFrame([['1:one', '2:one', '3:one', '4:one'],
                   ['1:two', '2:two', '3:two', '4:two'],
                   ['1:three', '2:three', '3:three', '4:three'],
                   ['1:four', '2:four', '3:four', '4:four']])

         0        1        2        3
0    1:one    2:one    3:one    4:one
1    1:two    2:two    3:two    4:two
2  1:three  2:three  3:three  4:three
3   1:four   2:four   3:four   4:four

Хотя я могу построить свою собственную матрицу через itertools.product, это кажется очень неэффективным способом для больших наборов данных, и мне было интересно, есть ли способ более pythoni c. Заранее спасибо.

Ответы [ 5 ]

9 голосов
/ 03 августа 2020

Попробуем np.add.outer

df = pd.DataFrame(np.add.outer(df1[0].astype(str).values,':'+df2[0].values).T)
Out[258]: 
         0        1        2        3
0    1:one    2:one    3:one    4:one
1    1:two    2:two    3:two    4:two
2  1:three  2:three  3:three  4:three
3   1:four   2:four   3:four   4:four
8 голосов
/ 03 августа 2020

Вы также можете использовать конструктор pd.DataFrame с apply:

pd.DataFrame(index=df2.squeeze(), columns=df1.squeeze()).apply(lambda x: x.name.astype(str)+':'+x.index)

Вывод:

            1        2        3        4                                        
one      1:one    2:one    3:one    4:one
two      1:two    2:two    3:two    4:two
three  1:three  2:three  3:three  4:three
four    1:four   2:four   3:four   4:four

Пояснение:

Во-первых, с конструктором pd.DataFrame, первой сборкой и пустым фреймом данных с индексом и столбцами, определенными из df2 и df1 соответственно. Используя pd.DataFrame.squeeze, мы конвертируем эти фреймы данных с одним столбцом в pd.Series.

Затем, используя pd.DataFrame.apply, мы можем применить лямбда-функцию, которая добавляет строки из имени столбца с двоеточием и фрейма данных индекс для каждого столбца фрейма данных.

Это дает новый фрейм данных с индексированием и желаемыми значениями.

5 голосов
/ 03 августа 2020

Другой способ использования np.tile:

pd.DataFrame(np.tile(df1[0][:,None],df2.shape[0])).astype(str).add(":"+df2[0]).T

Или аналогичный, но без преобразования любезно @ Ch3ster

pd.DataFrame(np.repeat(df1[0].astype(str)[None,:],df2.shape[0],axis=0)).add(':'+df2[0])
         0        1        2        3
0    1:one    2:one    3:one    4:one
1    1:two    2:two    3:two    4:two
2  1:three  2:three  3:three  4:three
3   1:four   2:four   3:four   4:four

EDIT,

Для использования вместе с вашей функцией вы также можете использовать перекрестное соединение:

def my_function(x, y):
    return f"{x}:{y}"

u = df1.assign(k=1).merge(df2.assign(k=1),on='k').drop('k',1).to_numpy()
arr = (np.array([*map(lambda x: my_function(*x),u)])
         .reshape((df1.shape[0],df2.shape[0]),order='F'))
print(arr,"\n---------------------------------------------------\n",pd.DataFrame(arr))

[['1:one' '2:one' '3:one' '4:one']
 ['1:two' '2:two' '3:two' '4:two']
 ['1:three' '2:three' '3:three' '4:three']
 ['1:four' '2:four' '3:four' '4:four']] 
---------------------------------------------------
         0        1        2        3
0    1:one    2:one    3:one    4:one
1    1:two    2:two    3:two    4:two
2  1:three  2:three  3:three  4:three
3   1:four   2:four   3:four   4:four
4 голосов
/ 03 августа 2020

Вы можете добавить их, но сгладить 1-й df, используя numpy.ndarray.ravel

pd.DataFrame(df1.astype(str).to_numpy().ravel() + ':' + df2.to_numpy())

         0        1        2        3
0    1:one    2:one    3:one    4:one
1    1:two    2:two    3:two    4:two
2  1:three  2:three  3:three  4:three
3   1:four   2:four   3:four   4:four
2 голосов
/ 03 августа 2020

Для полноты, приведенные выше ответы подходят для простых случаев использования. Для более сложных пользовательских функций это, вероятно, самый простой (хотя и несколько уродливый) вариант:

df = []
for i in df1.iterrows():
    row = [] 
    for j in df2.iterrows():
        row.append(my_function(i[1][0], j[1][0]))
    df.append(row)

pd.DataFrame(df)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...