R: Vectorize loop เพื่อสร้างเมทริกซ์แบบคู่

ฉันต้องการเร่งความเร็วฟังก์ชันสำหรับการสร้างเมทริกซ์แบบคู่ที่อธิบายจำนวนครั้งที่วัตถุถูกเลือกก่อนและหลังวัตถุอื่นๆ ทั้งหมด ภายในชุดของตำแหน่ง

นี่คือตัวอย่าง df:

  df <- data.frame(Shop = c("A","A","A","B","B","C","C","D","D","D","E","E","E"),
                   Fruit = c("apple", "orange", "pear",
                             "orange", "pear",
                             "pear", "apple",
                             "pear", "apple", "orange",
                             "pear", "apple", "orange"),
                   Order = c(1, 2, 3,
                            1, 2,
                            1, 2, 
                            1, 2, 3,
                            1, 1, 1))

ในแต่ละ Shop, Fruit จะถูกเลือกโดยลูกค้าใน Order ที่กำหนด

ฟังก์ชันต่อไปนี้สร้างเมทริกซ์แบบคู่ m x n:

loop.function <- function(df){
  
  fruits <- unique(df$Fruit)
  nt <- length(fruits)
  mat <- array(dim=c(nt,nt))
  
  for(m in 1:nt){
    
    for(n in 1:nt){
      
      ## filter df for each pair of fruit
      xm <- df[df$Fruit == fruits[m],]
      xn <- df[df$Fruit == fruits[n],]
      
      ## index instances when a pair of fruit are picked in same shop
      mm <- match(xm$Shop, xn$Shop)
      
      ## filter xm and xn based on mm
      xm <- xm[! is.na(mm),]
      xn <- xn[mm[! is.na(mm)],]
      
      ## assign number of times fruit[m] is picked after fruit[n] to mat[m,n]
      mat[m,n] <- sum(xn$Order < xm$Order)
    }
  }
  
  row.names(mat) <- fruits
  colnames(mat) <- fruits
  
  return(mat)
}

โดยที่ mat[m,n] คือจำนวนครั้งที่ fruits[m] ถูกเลือก หลัง fruits[n] และ mat[n,m] คือจำนวนครั้งที่ fruits[m] ถูกเลือก ก่อน fruits[n] จะไม่ถูกบันทึกหากเลือกผลไม้เป็นคู่พร้อมกัน (เช่น ใน Shop E)

ดูผลลัพธ์ที่คาดหวัง:

>loop.function(df)
       apple orange pear
apple      0      0    2
orange     2      0    1
pear       1      2    0

คุณจะเห็นได้ว่า pear ถูกเลือกสองครั้งก่อน apple (ใน Shop C และ D) และ apple ถูกเลือกหนึ่งครั้งก่อน pear (ใน Shop A)

ฉันกำลังพยายามปรับปรุงความรู้ของฉันเกี่ยวกับการทำให้เป็นเวกเตอร์ โดยเฉพาะอย่างยิ่งในส่วนของการวนซ้ำ ดังนั้นฉันจึงอยากรู้ว่าการวนซ้ำนี้สามารถทำให้เป็นเวกเตอร์ได้อย่างไร

(ฉันรู้สึกว่าอาจมีวิธีแก้ปัญหาโดยใช้ outer() แต่ความรู้เกี่ยวกับฟังก์ชันเวคเตอร์ของฉันยังมีจำกัดมาก)

อัปเดต

ดูการเปรียบเทียบด้วยข้อมูลจริง times = 10000 สำหรับ loop.function(), tidyverse.function(), loop.function2(), datatable.function() และ loop.function.TMS():

Unit: milliseconds
                    expr            min        lq       mean    median         uq      max     neval   cld
      loop.function(dat)     186.588600 202.78350 225.724249 215.56575 234.035750 999.8234    10000     e
     tidyverse.function(dat)  21.523400  22.93695  26.795815  23.67290  26.862700 295.7456    10000   c 
     loop.function2(dat)     119.695400 126.48825 142.568758 135.23555 148.876100 929.0066    10000    d
 datatable.function(dat)       8.517600   9.28085  10.644163   9.97835  10.766749 215.3245    10000  b 
  loop.function.TMS(dat)       4.482001   5.08030   5.916408   5.38215   5.833699  77.1935    10000 a 

ผลลัพธ์ที่น่าสนใจที่สุดสำหรับฉันอาจเป็นประสิทธิภาพของ tidyverse.function() จากข้อมูลจริง ฉันจะต้องลองเพิ่มโซลูชัน Rccp ในภายหลัง - ฉันมีปัญหาในการทำให้พวกเขาทำงานกับข้อมูลจริงได้

ฉันขอขอบคุณสำหรับความสนใจและคำตอบทั้งหมดที่มีให้กับโพสต์นี้ - ความตั้งใจของฉันคือการเรียนรู้และปรับปรุงประสิทธิภาพ และแน่นอนว่ายังมีอะไรอีกมากมายให้เรียนรู้จากความคิดเห็นและวิธีแก้ปัญหาทั้งหมดที่ได้รับ ขอบคุณ!


person jayb    schedule 08.07.2020    source แหล่งที่มา
comment
สวัสดี ชุดข้อมูลจริงของคุณมีขนาดเท่าใด และคุณจะเรียกใช้ฟังก์ชันนี้กี่ครั้ง   -  person chinsoon12    schedule 09.07.2020
comment
และควรสั่งซื้อเป็น 1,2,3 แทนที่จะเป็น 1,1,1 สำหรับ Shop E หรือไม่   -  person chinsoon12    schedule 09.07.2020
comment
ตามขนาด: โดยทั่วไปแล้ว df อาจมีผลไม้ประมาณ 15 ชิ้นที่สั่งในร้านค้าประมาณ 100 แห่ง มันถูกเรียกว่า ~1K ครั้งในการวิ่งครั้งเดียว อย่างไรก็ตาม ด้วยการบูตสแตรปปิ้งจะมีการวิ่ง 10,000 ครั้ง ที่ร้านค้า E: ไม่ นี่ไม่ใช่ข้อผิดพลาด ฉันต้องการให้ตัวอย่างรวมกรณีที่ผลไม้ทั้งหมดถูกเลือกพร้อมกัน เนื่องจากสิ่งสำคัญคือฟังก์ชันจะละเว้นกรณีเหล่านี้   -  person jayb    schedule 09.07.2020
comment
@ chinsoon12 มีความคล้ายคลึงกับคำถามนี้ แต่การสั่งซื้อในปัญหาของฉันเพิ่มความซับซ้อนอีกชั้น: ‹stackoverflow.com/questions/19891278/  -  person jayb    schedule 09.07.2020
comment
ปลอดภัยไหมที่จะสรุปว่าร้านค้าต่างๆ จะถูกจัดเรียงอยู่เสมอ? ถ้าไม่ จะปลอดภัยหรือไม่ที่จะจัดเรียงพวกมัน?   -  person Cole    schedule 11.07.2020
comment
@jayb ขอบคุณสำหรับการโพสต์ชุดข้อมูลของเล่นขนาดเล็กเพื่อให้ผู้คนลองใช้โค้ดของพวกเขา อย่างไรก็ตาม เนื่องจากคำถามของคุณเกี่ยวกับความเร็วและประสิทธิภาพ คุณช่วยระบุชุดข้อมูลขนาดและความซับซ้อนที่เกี่ยวข้องกับการเปรียบเทียบในคำถามของคุณได้ไหม หากไม่มีข้อมูลดังกล่าว จะประเมินคำตอบได้ยากหรือเป็นไปไม่ได้ อธิบายการปรับปรุงที่คุณคาดหวังด้วย ขอบคุณ   -  person Henrik    schedule 11.07.2020
comment
เนื่องจากลูปทำงานได้ดี ฉันจึงเพิ่มโซลูชัน Rcpp : ประสิทธิภาพที่ดีมาก   -  person Waldi    schedule 12.07.2020
comment
ลำดับจะเป็นเช่น c(1, 1, 2, 3) หรือจะเป็น c(1, 1, 1) เสมอหรือตามลำดับ   -  person Andrew    schedule 14.07.2020
comment
@jayb คำถามของคุณสร้างคำตอบมากมาย คุณสามารถอัปเดตการเปรียบเทียบบนชุดข้อมูลจริงได้หรือไม่ ขอบคุณสำหรับคำติชมของคุณ   -  person Waldi    schedule 16.07.2020
comment
@jayb ฉันกำลังแก้ไขปัญหาซึ่งทำให้นึกถึงคำถามของคุณ: ฉันคิดว่าแพ็คเกจ arulesSequence อาจเกี่ยวข้องกับคุณ ดูเช่น บทช่วยสอนนี้: การขุดรูปแบบตามลำดับใน R   -  person Henrik    schedule 20.08.2020


คำตอบ (4)


ดูเหมือนว่าเป็นไปไม่ได้ที่จะทำเวคเตอร์เหนือกรอบข้อมูลดั้งเดิม df แต่ถ้าคุณแปลงมันโดยใช้ reshape2::dcast() เพื่อให้มีหนึ่งบรรทัดต่อร้านค้าแต่ละร้าน:

require(reshape2)

df$Fruit <- as.character(df$Fruit)

by_shop <- dcast(df, Shop ~ Fruit, value.var = "Order")

#   Shop apple orange pear
# 1    A     1      2    3
# 2    B    NA      1    2
# 3    C     2     NA    1
# 4    D     2      3    1
# 5    E     1      1    1

... จากนั้นคุณก็สามารถ vectorize อย่างน้อยสำหรับการรวมกันของ [m, n] แต่ละอันได้อย่างง่ายดาย:

fruits <- unique(df$Fruit)
outer(fruits, fruits, 
    Vectorize(
        function (m, n, by_shop) sum(by_shop[,m] > by_shop[,n], na.rm = TRUE), 
        c("m", "n")
    ), 
    by_shop)
#      [,1] [,2] [,3]
# [1,]    0    0    2
# [2,]    2    0    1
# [3,]    1    2    0

นี่อาจเป็นวิธีแก้ปัญหาที่คุณต้องการทำกับ outer วิธีแก้ปัญหาที่เร็วกว่ามากจะเป็นเวกเตอร์ไดเซชันที่แท้จริงของผลรวมของผลไม้ทั้งหมด [m, n] แต่ฉันคิดเรื่องนี้อยู่ และฉันไม่เห็นวิธีที่จะทำเลย ฉันเลยต้องใช้ฟังก์ชัน Vectorize ซึ่งแน่นอนว่าช้ากว่าการทำเวกเตอร์จริงมาก

การเปรียบเทียบเกณฑ์มาตรฐานกับฟังก์ชันเดิมของคุณ:

Unit: milliseconds
                  expr      min       lq     mean   median       uq      max neval
     loop.function(df) 3.788794 3.926851 4.157606 4.002502 4.090898 9.529923   100
 loop.function.TMS(df) 1.582858 1.625566 1.804140 1.670095 1.756671 8.569813   100

ฟังก์ชั่นและรหัสมาตรฐาน (เพิ่มการเก็บรักษา dimnames ด้วย):

require(reshape2)   
loop.function.TMS <- function(df) { 
    df$Fruit <- as.character(df$Fruit)
    by_shop <- dcast(df, Shop ~ Fruit, value.var = "Order")
    fruits <- unique(df$Fruit)
    o <- outer(fruits, fruits, Vectorize(function (m, n, by_shop) sum(by_shop[,m] > by_shop[,n], na.rm = TRUE), c("m", "n")), by_shop)
    colnames(o) <- rownames(o) <- fruits
    o
}

require(microbenchmark)
microbenchmark(loop.function(df), loop.function.TMS(df))
person Tomas    schedule 15.07.2020
comment
ขอบคุณสำหรับสิ่งนี้ - เป็นการใช้ outer() ที่น่าสนใจจริงๆ - ฉันไม่เคยเห็นมันใช้กับ Vectorize() ในลักษณะนี้มาก่อน - person jayb; 16.07.2020

วิธีแก้ปัญหา data.table :

library(data.table)
setDT(df)
setkey(df,Shop)
dcast(df[df,on=.(Shop=Shop),allow.cartesian=T][
           ,.(cnt=sum(i.Order<Order&i.Fruit!=Fruit)),by=.(Fruit,i.Fruit)]
      ,Fruit~i.Fruit,value.var='cnt')

    Fruit apple orange pear
1:  apple     0      0    2
2: orange     2      0    1
3:   pear     1      2    0

ดัชนี Shop ไม่จำเป็นสำหรับตัวอย่างนี้ แต่อาจจะปรับปรุงประสิทธิภาพชุดข้อมูลขนาดใหญ่ขึ้นได้

เนื่องจากคำถามทำให้เกิดความคิดเห็นมากมายเกี่ยวกับประสิทธิภาพ ฉันจึงตัดสินใจตรวจสอบว่า Rcpp สามารถนำมาซึ่งอะไรได้บ้าง:

library(Rcpp)
cppFunction('NumericMatrix rcppPair(DataFrame df) {

std::vector<std::string> Shop = Rcpp::as<std::vector<std::string> >(df["Shop"]);
Rcpp::NumericVector Order = df["Order"];
Rcpp::StringVector Fruit = df["Fruit"];
StringVector FruitLevels = sort_unique(Fruit);
IntegerVector FruitInt = match(Fruit, FruitLevels);
int n  = FruitLevels.length();

std::string currentShop = "";
int order, fruit, i, f;

NumericMatrix result(n,n);
NumericVector fruitOrder(n);

for (i=0;i<Fruit.length();i++){
    if (currentShop != Shop[i]) {
       //Init counter for each shop
       currentShop = Shop[i];
       std::fill(fruitOrder.begin(), fruitOrder.end(), 0);
    }
    order = Order[i];
    fruit = FruitInt[i];
    fruitOrder[fruit-1] = order;
    for (f=0;f<n;f++) {
       if (order > fruitOrder[f] & fruitOrder[f]>0 ) { 
         result(fruit-1,f) = result(fruit-1,f)+1; 
    }
  }
}
rownames(result) = FruitLevels;
colnames(result) = FruitLevels;
return(result);
}
')

rcppPair(df)

       apple orange pear
apple      0      0    2
orange     2      0    1
pear       1      2    0

ในชุดข้อมูลตัวอย่าง สิ่งนี้จะทำงานได้ >เร็วกว่า 500 เท่า กว่าโซลูชัน data.table อาจเป็นเพราะมันไม่มีปัญหาผลิตภัณฑ์คาร์ทีเซียน สิ่งนี้ไม่ควรมีประสิทธิภาพหากป้อนข้อมูลที่ไม่ถูกต้อง และคาดว่าร้านค้า/คำสั่งซื้อจะเรียงลำดับจากน้อยไปหามาก

เมื่อพิจารณาถึงการสละเวลาไม่กี่นาทีเพื่อค้นหาโค้ด 3 บรรทัดสำหรับโซลูชัน data.table เมื่อเปรียบเทียบกับกระบวนการแก้ปัญหา / การแก้ไขจุดบกพร่อง Rcpp ที่ยาวกว่ามาก ฉันไม่แนะนำให้ใช้ Rcpp ที่นี่ เว้นแต่จะมีปัญหาคอขวดด้านประสิทธิภาพที่แท้จริง

อย่างไรก็ตาม น่าสนใจที่ต้องจำไว้ว่าหากประสิทธิภาพเป็นสิ่งจำเป็น Rcpp อาจจะคุ้มค่ากับความพยายาม

person Waldi    schedule 10.07.2020
comment
Code-golf (ซึ่งท้ายที่สุดจะเท่ากับความเร็ว): ฉันไม่รู้ว่า &i.Fruit!=Fruit กำลังเปลี่ยนแปลงอะไรกับข้อมูลตัวอย่างนี้ คุณแน่ใจหรือไม่ว่ามันจำเป็นในเชิงตรรกะ? - person r2evans; 11.07.2020
comment
@ r2evans ฉันคิดว่านี่คงจะมีประโยชน์ที่จะไม่นับเช่น Apple ที่คว้าอันดับหนึ่งและอันดับสามโดยมีสีส้มอยู่ระหว่างนั้น จากคำอธิบายผมเข้าใจว่าเราต้องการนับผลไม้ที่แตกต่างกันเท่านั้น - person Waldi; 11.07.2020
comment
ฉันก็คิดอย่างนั้นเหมือนกัน และนั่นเป็นแนวทางที่ปลอดภัยกว่าอย่างแน่นอน หาก OP รับรองว่าอินพุตนั้นไม่ซ้ำกันอยู่แล้ว per-Shop ฉันคิดว่าคำแนะนำของฉันคงอยู่ (มันไม่ยากที่จะตรวจสอบให้แน่ใจก่อน matrixification. - person r2evans; 11.07.2020
comment
โปรดทราบว่าบนเครื่องของฉัน โซลูชันของคุณ (ฟังก์ชันการทำงาน) ใช้เวลามากกว่าครึ่งหนึ่งของ loop.function (เนื่องจาก OP ต้องการ เพิ่มความเร็วฟังก์ชัน) - person r2evans; 11.07.2020
comment
@ r2evans ขอบคุณสำหรับการเปรียบเทียบ เห็นผลด้วยชุดข้อมูลจริงจะน่าสนใจ (100 ร้านค้า / 15 ผลไม้) - person Waldi; 11.07.2020
comment
อาจเพิ่มเงื่อนไขที่ไม่เทียบเท่าในการรวมครั้งแรก เพื่อรวมเฉพาะชุดค่าผสม 'คำสั่งซื้อ' ที่เกี่ยวข้องเท่านั้น (และหลีกเลี่ยงการระเบิด allow.cartesian) จากนั้นการคำนวณในขั้นตอนที่สองสามารถทำให้ง่ายขึ้นในการนับแถว d = df[df, on = .(Shop, Order < Order), .(before = Fruit, after = i.Fruit)]; dcast(d[!is.na(before), .N, by = .(before, after)], after ~ before, value.var = "N", fill = 0). - person Henrik; 11.07.2020
comment
@Henrik ขอบคุณสำหรับข้อเสนอแนะของคุณ ฉันเข้าใจประเด็นของคุณและทำให้ฉันคิดเกี่ยวกับเรื่องนี้ด้วย ฉันสรุปได้ว่าการระเบิดคาร์ทีเซียนนั้นจะถูกจำกัดโดยร้านค้าเข้าร่วม ในแง่ของการวัดประสิทธิภาพระดับไมโคร สำหรับชุดข้อมูลตัวอย่าง โซลูชันของคุณช้ากว่าเล็กน้อย แต่แน่นอนว่าสิ่งนี้จะต้องได้รับการทดสอบกับชุดข้อมูลจริง ซึ่งควรมองเห็นข้อดีของชุดข้อมูลได้ชัดเจนยิ่งขึ้น - person Waldi; 11.07.2020
comment
ด้วยคำแนะนำ @Henriks คุณสามารถรับผลลัพธ์เดียวกันกับ table(i.Fruit, Fruit) เป็น j สำหรับแนวทางที่ไม่เท่าเทียมกัน ดังนั้น df[df, on = .(Shop, Order < Order), table(i.Fruit, Fruit), allow.cartesian = T, nomatch = 0L]. ฉันสนใจคำตอบของคุณในโลกแห่งความเป็นจริงเช่นกัน - การเปรียบเทียบมาตรฐานเป็นมิลลิวินาทีแทบจะไม่บอกเลย - person Cole; 11.07.2020
comment
@Waldi ขอบคุณมากสำหรับโซลูชันนี้ - ฉันไม่คุ้นเคยกับ data.table แต่น่าจะเป็นเช่นนั้นเมื่อได้รับประสิทธิภาพ ปัญหาอย่างหนึ่งในการใช้โซลูชันนี้ในอัลกอริธึมจริงของฉันก็คือเอาต์พุตเป็นรายการ ซึ่งฉันไม่สามารถจัดทำดัชนีในลักษณะเดียวกับเมทริกซ์ได้ - person jayb; 16.07.2020
comment
@jayb หากคุณกำลังมองหา ประสิทธิภาพ data.table ก็คุ้มค่าที่จะลอง ). การแปลงเป็นเมทริกซ์ทำได้ง่ายเพียง : as.matrix(dt) - person Waldi; 16.07.2020

ต่อไปนี้เป็นแนวทางที่ทำการปรับเปลี่ยนง่ายๆ เพื่อให้เร็วขึ้น 5 เท่า

loop.function2 <- function(df){

    spl_df = split(df[, c(1L, 3L)], df[[2L]])
    
    mat <- array(0L,
                 dim=c(length(spl_df), length(spl_df)),
                 dimnames = list(names(spl_df), names(spl_df)))
    
    for (m in 1:(length(spl_df) - 1L)) {
        xm = spl_df[[m]]
        mShop = xm$Shop
        for (n in ((1+m):length(spl_df))) {
            xn = spl_df[[n]]
            mm = match(mShop, xn$Shop)
            inds = which(!is.na(mm))
            mOrder = xm[inds, "Order"]
            nOrder = xn[mm[inds], "Order"]

            mat[m, n] <- sum(nOrder < mOrder)
            mat[n, m] <- sum(mOrder < nOrder)
        }
    }
    mat
}

มี 3 แนวคิดหลัก:

  1. บรรทัด df[df$Fruits == fruits[m], ] ดั้งเดิมไม่มีประสิทธิภาพ เนื่องจากคุณจะต้องทำการเปรียบเทียบเดียวกัน length(Fruits)^2 ครั้ง แต่เราสามารถใช้ split() แทนได้ ซึ่งหมายความว่าเราจะสแกนผลไม้เพียงครั้งเดียวเท่านั้น
  2. มีการใช้ df$var เป็นจำนวนมากซึ่งจะแยกเวกเตอร์ระหว่างแต่ละลูป ที่นี่ เราวางการกำหนด xm ไว้นอกลูปด้านใน และเราพยายามลดสิ่งที่เราจำเป็นต้องย่อย / แยกให้เหลือน้อยที่สุด
  3. ฉันเปลี่ยนให้เข้าใกล้ combn มากขึ้น เพราะเราสามารถใช้เงื่อนไข match() ของเราซ้ำได้โดยทำทั้งสองอย่าง sum(xmOrder > xnOrder) แล้วเปลี่ยนเป็น sum(xmOrder < xnOrder)

ผลงาน:

bench::mark(loop.function(df), loop.function2(df))

# A tibble: 2 x 13
##  expression              min median
##  <bch:expr>         <bch:tm> <bch:>
##1 loop.function(df)    3.57ms 4.34ms
##2 loop.function2(df)  677.2us 858.6us

ลางสังหรณ์ของฉันคือสำหรับชุดข้อมูลขนาดใหญ่ของคุณ โซลูชัน data.table จะเร็วขึ้น แต่สำหรับชุดข้อมูลขนาดเล็ก สิ่งนี้ควรจะค่อนข้างมีประสิทธิภาพ

สุดท้ายนี้ นี่เป็นแนวทาง rcpp อีกแนวทางหนึ่งที่ดูเหมือนว่าจะเป็น ช้ากว่า @Waldi:

#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
IntegerMatrix loop_function_cpp(List x) {
    int x_size = x.size();
    IntegerMatrix ans(x_size, x_size);
    
    for (int m = 0; m < x_size - 1; m++) {
        DataFrame xm = x[m];
        CharacterVector mShop = xm[0];
        IntegerVector mOrder = xm[1];
        int nrows = mShop.size();
        for (int n = m + 1; n < x_size; n++) {
            DataFrame xn = x[n];
            CharacterVector nShop = xn[0];
            IntegerVector nOrder = xn[1];
            for (int i = 0; i < nrows; i++) {
                for (int j = 0; j < nrows; j++) {
                    if (mShop[i] == nShop[j]) {
                        if (mOrder[i] > nOrder[j])
                           ans(m, n)++;
                        else
                            ans(n, m)++;
                        break;
                    }
                }
            }
        }
    }
    return(ans);
}
loop_wrapper = function(df) {
  loop_function_cpp(split(df[, c(1L, 3L)], df[[2L]]))
}
loop_wrapper(df)
``
person Cole    schedule 11.07.2020
comment
ขอบคุณมากสำหรับวิธีแก้ปัญหา - มีหลายสิ่งที่ต้องเรียนรู้อย่างแน่นอนในแง่ของการเร่งความเร็วลูปที่มีอยู่ที่นี่ โดยเฉพาะอย่างยิ่งการเก็บเฉพาะโค้ดที่จำเป็นจริงๆ ไว้ในแต่ละลูปเท่านั้น - person jayb; 16.07.2020

ตกลง นี่คือวิธีแก้ปัญหา:

library(tidyverse)

# a dataframe with all fruit combinations
df_compare <-  expand.grid(row_fruit = unique(df$Fruit)
                           , column_fruit = unique(df$Fruit)
                           , stringsAsFactors = FALSE)

df_compare %>%
    left_join(df, by = c("row_fruit" = "Fruit")) %>%
    left_join(df, by = c("column_fruit" = "Fruit")) %>%
    filter(Shop.x == Shop.y &
               Order.x < Order.y) %>%
    group_by(row_fruit, column_fruit) %>%
    summarise(obs = n()) %>%
    pivot_wider(names_from = row_fruit, values_from = obs) %>%
    arrange(column_fruit) %>%
    mutate_if(is.numeric, function(x) replace_na(x, 0)) %>%
    column_to_rownames("column_fruit") %>%
    as.matrix()

       apple orange pear
apple      0      0    2
orange     2      0    1
pear       1      2    0

หากคุณไม่รู้ว่าเกิดอะไรขึ้นในส่วนโค้ดที่สอง (df_compare %>% ...) ให้อ่านไปป์ (%>%) ว่า 'แล้ว' รันโค้ดจาก df_compare ไปที่หน้าไปป์ใดๆ เพื่อดูผลลัพธ์ระดับกลาง

person Georgery    schedule 08.07.2020
comment
ขอบคุณสำหรับวิธีแก้ปัญหาที่แนะนำ ฉันควรจะชี้แจงว่าสิ่งสำคัญคือโครงสร้าง (และลำดับ) ของเอาต์พุตจะถูกเก็บรักษาไว้ตามเอาต์พุตของ loop.function() - person jayb; 08.07.2020
comment
ฉันทำการเปลี่ยนแปลงโค้ดของคุณเพื่อให้ส่งคืนเอาต์พุตเดียวกันกับ loop.function() ติดตาม column_to_rownames("row_fruit") %>% ฉันเพิ่ม ` select(all_of(unique(df$Fruit))) %›%` และติดตาม as.matrix() ฉันเพิ่ม %>% replace_na(replace = 0) ตอนนี้โค้ดส่งคืนเอาต์พุตเดียวกัน แต่ไม่ได้ปรับปรุงความเร็ว - เหตุผลที่ฉันสนใจในการทำเวกเตอร์นั้นเกี่ยวข้องกับประสิทธิภาพ ฉันได้เพิ่มการเปรียบเทียบตามโค้ดของคุณ (พร้อมการแก้ไข) - person jayb; 08.07.2020
comment
ดังนั้นฉันจึงแก้ไขคำตอบ อยากทำเมื่อวานแต่ stackoverflow หยุดทำงาน ฉันทดสอบแล้วและ loop.function() เร็วขึ้น - สำหรับชุดข้อมูลที่ใหญ่กว่าด้วย อย่างไรก็ตาม มันแปลกนิดหน่อยที่จะเปลี่ยนคำถามในลักษณะนั้น คุณถามเกี่ยวกับการทำเวกเตอร์ ไม่ใช่เกี่ยวกับประสิทธิภาพ สำหรับคำถามเดิม คำตอบของฉันคือคำตอบ - person Georgery; 09.07.2020
comment
สวัสดี ฉันเพียงเปลี่ยนโพสต์ต้นฉบับเพื่อชี้แจงบางแง่มุมของคำถามตามที่คุณร้องขอ และเพื่อเพิ่มการเปรียบเทียบสำหรับโค้ดของคุณ โพสต์ถูกแท็กด้วยประสิทธิภาพเสมอและบรรทัดแรกระบุเสมอว่าฉันต้องการเร่งความเร็วฟังก์ชัน... - person jayb; 09.07.2020