como executar uma junção interna ou externa de DataFrames com Pandas em um critério não simplista

Dados dois quadros de dados como abaixo:

>>> import pandas as pd

>>> df_a = pd.DataFrame([{"a": 1, "b": 4}, {"a": 2, "b": 5}, {"a": 3, "b": 6}])
>>> df_b = pd.DataFrame([{"c": 2, "d": 7}, {"c": 3, "d": 8}])
>>> df_a
   a  b
0  1  4
1  2  5
2  3  6

>>> df_b
   c  d
0  2  7
1  3  8

Gostaríamos de produzir uma junção no estilo SQL de ambos os dataframes usando um critério não simplista, digamos "df_b.c> df_a.a". Pelo que posso dizer, enquantomerge() certamente é parte da solução, não posso usá-la diretamente, já que ela não aceita expressões arbitrárias para critérios "ON" (a menos que eu esteja perdendo alguma coisa?).

No SQL, os resultados são assim:

# inner join
sqlite> select * from df_a join df_b on c > a;
1|4|2|7
1|4|3|8
2|5|3|8

# outer join
sqlite> select * from df_a left outer join df_b on c > a;
1|4|2|7
1|4|3|8
2|5|3|8
3|6||

Minha abordagem atual para junção interna é produzir um produto cartesiano de df_a e df_b, adicionando uma coluna de "1" s para ambos, em seguida, usando merge () na coluna "1", em seguida, aplicando o "c> a" critério.

>>> import numpy as np
>>> df_a['ones'] = np.ones(3)
>>> df_b['ones'] = np.ones(2)
>>> cartesian = pd.merge(df_a, df_b, left_on='ones', right_on='ones')
>>> cartesian
   a  b  ones  c  d
0  1  4     1  2  7
1  1  4     1  3  8
2  2  5     1  2  7
3  2  5     1  3  8
4  3  6     1  2  7
5  3  6     1  3  8
>>> cartesian[cartesian.c > cartesian.a]
   a  b  ones  c  d
0  1  4     1  2  7
1  1  4     1  3  8
3  2  5     1  3  8

para outer join, eu não tenho certeza do melhor caminho a seguir, até agora eu tenho brincado com a junção interna, então aplicando a negação dos critérios para obter todas as outras linhas, então tentando editar essa "negação". "definido para o original, mas realmente não funciona.

Editar. HYRY respondeu à pergunta específica aqui, mas eu precisava de algo mais genérico e mais dentro da API do Pandas, já que meu critério de associação poderia ser qualquer coisa, não apenas aquela comparação. Para outerjoin, primeiro adiciono um índice extra ao lado "esquerdo" que se manterá depois que eu fizer a junção interna:

df_a['_left_index'] = df_a.index

então fazemos o cartesiano e obtemos a junção interna:

cartesian = pd.merge(df_a, df_b, left_on='ones', right_on='ones')
innerjoin = cartesian[cartesian.c > cartesian.a]

então eu recebo os ids de índice adicionais em "df_a" que precisaremos e obtemos as linhas de "df_a":

remaining_left_ids = set(df_a['_left_index']).\
                    difference(innerjoin['_left_index'])
remaining = df_a.ix[remaining_left_ids]

então usamos uma concat reta (), que substitui as colunas ausentes por "NaN" para a esquerda (achei que não estava fazendo isso antes, mas acho que não):

outerjoin = pd.concat([innerjoin, remaining]).reset_index()

A idéia de HYRY de fazer o cartesiano apenas com as cols com as quais precisamos comparar é basicamente a resposta certa, embora no meu caso específico possa ser um pouco complicado de implementar (generalizado e tudo).

questões:

Como você produziria uma "junção" de df_1 e df_2 em "c> a"? Você faria a mesma abordagem de "produto cartesiano, filtro" ou há alguma maneira melhor?

Como você produziria a "junção externa esquerda" da mesma?

questionAnswers(2)

yourAnswerToTheQuestion