Получить такое же перекрестное соединение с помощью merge () и sqldf :: sqldf () - PullRequest
1 голос
/ 01 мая 2020

У меня есть два фрейма данных: Sales и Clients. Я хочу выполнить перекрестные объединения этих фреймов данных, используя sqldf::sqldf(), а также merge() и получить одинаковый результат с обоими методами.

До сих пор мне удалось получить только два фрейма данных с строки упорядочены по-разному.

Это код для генерации фреймов данных Sales и Clients:

set.seed(1)

Sales <- data.frame(
  Product = sample(c("Toaster", "Radio", "TV"), size = 7, replace = TRUE),
  CustomerID = c(rep("1_2019", 2), paste(2:3, "2019", sep = "_"), paste(1:3, "2020", sep = "_"))
  )

Sales$Price <- round(ifelse(Sales$Product == "TV", rnorm(1, 400, 20),
                            ifelse(Sales$Product == "Toaster", rnorm(1, 40, 2), 
                                   rnorm(1, 35, 2))))

Clients <- data.frame(
  CustomerID = c(paste(2:4, "2019", sep = "_"), paste(1:2, "2020", sep = "_")),
  State = sample(c("CA", "AZ", "IL", "MA"), size = 5, replace = TRUE)
  )

Вот что я получил:


library(sqldf)

# cross join with base R
out1 <- merge(x = Sales, y = Clients, by = NULL)

# cross join with sqldf      
out2 <- sqldf("SELECT *
               FROM Sales
               CROSS JOIN Clients")

out1 и out2 имеют разные порядки строк. Как я могу настроить вызов sqldf(), чтобы out1 и out2 были одинаковыми?

Это самое близкое, что я получил:

merge(x = Sales, y = Clients, by = NULL)  

sqldf("SELECT *
       FROM Sales
       CROSS JOIN Clients 
       ORDER BY State DESC, Clients.CustomerID")

1 Ответ

2 голосов
/ 01 мая 2020

Я думаю, что включение ORDER BY в sqldf важно, поскольку оно указывает на тот факт, что в SQL упорядочение никогда не гарантируется, если явно не указано иное.

Если вы выполняли простое ORDER BY просто "увеличивая" обе переменные, перевод на order в R будет прямым. Однако, поскольку одна переменная уменьшается, а другая увеличивается, order сама по себе не справляется с этим. Однако, как предполагает { ссылка }, мы можем сделать то же самое с xtfrm.

out1 <- merge(x = Sales, y = Clients, by = NULL)
out1 <- out1[order(-xtfrm(out1$State), out1$CustomerID.y),]

out2 <- sqldf::sqldf(
  "SELECT *
   FROM Sales
   CROSS JOIN Clients 
   ORDER BY State DESC, Clients.CustomerID")

### proof they are identical
all(unlist(Map(`==`, out1, out2)))
# [1] TRUE

Вспомогательная функция xtfrm здесь позволяет нам отрицать «значения» столбец для целей сортировки. From ?xtfrm:

Универсальная вспомогательная функция c, которая генерирует вектор цифр c, который будет сортироваться в том же порядке, что и 'x'.

Если бы поле уже числилось c, мы могли бы просто сделать order(-State, CustomerID.y), но тот факт, что оно character, требует дальнейшего шага. Ar go xtfrm.


Редактировать : в комментариях определено, что ОП хочет имитировать c порядок сортировки merge в SQL заявление. К сожалению, поскольку это декартово произведение двух кадров, сортировка не применяется: merge просто cbind s все строки первого кадра относительно первого ряда второго кадра, а затем повторяется с каждым рядом второго кадра.

Это можно продемонстрировать, используя некоторый код из merge:

nx <- nrow(x) # Sales
ny <- nrow(y) # Clients
expand.grid(seq_len(nx), seq_len(ny))
#    Var1 Var2
# 1     1    1
# 2     2    1
# 3     3    1
# 4     4    1
# 5     5    1
# 6     1    2
# ...
# 33    3    7
# 34    4    7
# 35    5    7

, где каждое число является строкой из соответствующих кадров (x для Var1, y для Var2). Если исходные данные:

## Sales                        ## Clients        
  Product CustomerID Price        CustomerID State
1 Toaster     1_2019    37      1     2_2019    AZ
2   Radio     1_2019    33      2     3_2019    MA
3   Radio     2_2019    33      3     4_2019    AZ
4      TV     3_2019   408      4     1_2020    IL
5 Toaster     1_2020    37      5     2_2020    MA
6      TV     2_2020   408
7      TV     3_2020   408

, то это приводит к

out1
#    Product CustomerID.x Price CustomerID.y State
# 1  Toaster       1_2019    37       2_2019    AZ
# 2    Radio       1_2019    33       2_2019    AZ
# 3    Radio       2_2019    33       2_2019    AZ
# 4       TV       3_2019   408       2_2019    AZ
# 5  Toaster       1_2020    37       2_2019    AZ
# 6       TV       2_2020   408       2_2019    AZ
# 7       TV       3_2020   408       2_2019    AZ
# 8  Toaster       1_2019    37       3_2019    MA
# ...
# 33 Toaster       1_2020    37       2_2020    MA
# 34      TV       2_2020   408       2_2020    MA
# 35      TV       3_2020   408       2_2020    MA

, что очень сильно разрушит любую сортировку, присутствующую в x (Sales), даже если y (Clients) поставляется предварительно отсортированным (что он и делает).

Из-за этого , если вам нужно соответствие между решениями R и SQL перекрестного соединения, я предлагаю самый прозрачный / прозрачный способ - использовать merge в R, а затем применять порядок после merge таким же образом, как SQL. На самом деле, с точки зрения педагога, задайте вопрос: «Какой порядок имеет смысл для людей?» Если вы утверждаете во время плана урока, что порядок не может быть гарантирован до тех пор, пока он явно не будет решительно вовлечен в процесс (с помощью пункта dplyr::arrange или ORDER BY *1062*). Найдите интуитивно понятный порядок данных и затем продемонстрируйте, что как в R, так и в SQL.

Примечания:

  1. Ваш запрос sqldf приводит к столбцам с таким же именем, это приводит к некоторым ошибкам после sqldf, если вы начинаете играть с колонками. Это можно исправить с помощью select ... as ... именования полей.
  2. Lexicographi c сортировка ваших данных, к сожалению, на данный момент нелогична: наличие года в конце идентификатора клиента предлагает (да, я предполагаю) график регистрации клиентов, но они будут отсортированы в первую очередь по количеству ведущих. Подобно тому, как "2020-05-04" сортирует правильно даже в виде строки, в то время как "05/04/2020" - нет, он может поддерживать более интуитивную сортировку, чтобы наиболее значимая часть была ведущей частью строк идентификатора. Или сделайте их целыми числами. Или UUID (v4, конечно), это всегда весело.
...