r - একাধিক কলাম এবং প্রান্তিকের উপর ভিত্তি করে ডেটাফ্রেমগুলি মার্জ করুন




dataframe (4)

আমার একাধিক সাধারণ কলাম (এখানে: date , city , ctry , এবং ( other_ ) number ) সহ other_

আমি এখন উপরের কলামগুলিতে সেগুলি মার্জ করতে চাই তবে কিছু স্তর পার্থক্য সহ্য করতে চাই:

threshold.numbers <- 3
threshold.date <- 5  # in days

date এন্ট্রিগুলির মধ্যে পার্থক্য যদি হয় > threshold.date (দিনগুলিতে) বা > threshold.numbers আমি লাইনগুলিকে একীভূত করতে চাই না। একইভাবে, city প্রবেশ যদি city কলামে অন্য df এর প্রবেশের একটি স্ট্রিং থাকে, আমি লাইনগুলি একত্রিত করতে চাই। [যদি কারও কাছে প্রকৃত শহরের নামের মিলের জন্য পরীক্ষা করার জন্য আরও ভাল ধারণা থাকে তবে আমি এটি সম্পর্কে শুনে খুশি হব]] (এবং প্রথম other_ date , city এবং country এন্ট্রিগুলি রাখুন তবে উভয় ( other_ ) number কলাম এবং অন্যান্য সমস্ত কলাম df

নিম্নলিখিত উদাহরণ বিবেচনা করুন:

df1 <- data.frame(date = c("2003-08-29", "1999-06-12", "2000-08-29", "1999-02-24", "2001-04-17",
                           "1999-06-30", "1999-03-16", "1999-07-16", "2001-08-29", "2002-07-30"),
                  city = c("Berlin", "Paris", "London", "Rome", "Bern",
                           "Copenhagen", "Warsaw", "Moscow", "Tunis", "Vienna"),
                  ctry = c("Germany", "France", "UK", "Italy", "Switzerland",
                           "Denmark", "Poland", "Russia", "Tunisia", "Austria"),
                  number = c(10, 20, 30, 40, 50, 60, 70, 80, 90, 100),
                  col = c("apple", "banana", "pear", "banana", "lemon", "cucumber", "apple", "peach", "cherry", "cherry"))


df2 <- data.frame(date = c("2003-08-29", "1999-06-12", "2000-08-29", "1999-02-24", "2001-04-17", # all identical to df1
                           "1999-06-29", "1999-03-14", "1999-07-17", # all 1-2 days different
                           "2000-01-29", "2002-07-01"), # all very different (> 2 weeks)
                  city = c("Berlin", "East-Paris", "near London", "Rome", # same or slight differences
                           "Zurich", # completely different
                           "Copenhagen", "Warsaw", "Moscow", "Tunis", "Vienna"), # same
                  ctry = c("Germany", "France", "UK", "Italy", "Switzerland", # all the same 
                           "Denmark", "Poland", "Russia", "Tunisia", "Austria"),
                  other_number = c(13, 17, 3100, 45, 51, 61, 780, 85, 90, 101), # slightly different to very different
                  other_col = c("yellow", "green", "blue", "red", "purple", "orange", "blue", "red", "black", "beige"))

এখন, আমি data.frames মার্জ করতে চাই এবং উপরের শর্তগুলি পূরণ করা হলে লাইনগুলি একত্রিত করা হয়েছে এমন একটি df গ্রহণ করব।

(প্রথম কলামটি কেবল আপনার সুবিধার জন্য: প্রথম অঙ্কের পিছনে, যা মূল ক্ষেত্রেটি নির্দেশ করে, এটি দেখায় যে লাইনগুলি কোথায় মিশে গেছে ( . ) অথবা লাইনগুলি df1 ( 1 ) বা df2 ( 2 ) থেকে রয়েছে কিনা।

          date        city        ctry number other_col other_number    other_col2          #comment
 1.  2003-08-29      Berlin     Germany     10     apple              13        yellow      # matched on date, city, number
 2.  1999-06-12       Paris      France     20    banana              17         green      # matched on date, city similar, number - other_number == threshold.numbers
 31  2000-08-29      London          UK     30      pear            <NA>          <NA>      # not matched: number - other_number > threshold.numbers
 32  2000-08-29 near London         UK    <NA>      <NA>            3100          blue      #
 41  1999-02-24        Rome       Italy     40    banana            <NA>          <NA>      # not matched: number - other_number > threshold.numbers
 42  1999-02-24        Rome       Italy   <NA>      <NA>              45           red      #
 51  2001-04-17        Bern Switzerland     50     lemon            <NA>          <NA>      # not matched: cities different (dates okay, numbers okay)
 52  2001-04-17      Zurich Switzerland   <NA>      <NA>              51        purple      #
 6.  1999-06-30  Copenhagen     Denmark     60  cucumber              61        orange      # matched: date difference < threshold.date (cities okay, dates okay)
 71  1999-03-16      Warsaw      Poland     70     apple            <NA>          <NA>      # not matched: number - other_number > threshold.numbers (dates okay)
 72  1999-03-14      Warsaw      Poland   <NA>      <NA>             780          blue      # 
 81  1999-07-16      Moscow      Russia     80     peach            <NA>          <NA>      # not matched: number - other_number > threshold.numbers (dates okay)
 82  1999-07-17      Moscow      Russia   <NA>      <NA>              85           red      #
 91  2001-08-29       Tunis     Tunisia     90    cherry            <NA>          <NA>      # not matched: date difference < threshold.date (cities okay, dates okay)
 92  2000-01-29       Tunis     Tunisia   <NA>      <NA>              90         black      #
101  2002-07-30      Vienna     Austria    100    cherry            <NA>          <NA>      # not matched: date difference < threshold.date (cities okay, dates okay)
102  2002-07-01      Vienna     Austria   <NA>      <NA>             101         beige      #

আমি তাদের মার্জ করার বিভিন্ন বাস্তবায়নের চেষ্টা করেছি কিন্তু প্রান্তিকর বাস্তবায়ন করতে পারছি না।

অস্পষ্ট গঠনের জন্য ক্ষমা প্রার্থনা সম্পাদনা করুন - আমি সমস্ত সারি ধরে রাখতে চাই এবং সারিটি মেলে কিনা, ডিফল 1 বা তুলনামূলকভাবে এবং ডিএফ 2 থেকে একটি সূচক পেতে চাই।

সিউডো কোডটি হ'ল:

  if there is a case where abs("date_df2" - "date_df1") <= threshold.date:
    if "ctry_df2" == "ctry_df1":
      if "city_df2" ~ "city_df1":
        if abs("number_df2" - "number_df1") <= threshold.numbers:
          merge and go to next row in df2
  else:
    add row to df1```

আপনি grepl এবং ctry সাথে == সাথে grepl মিলটি পরীক্ষা করতে পারেন। যারা as.Date date as.Date আপনি তাদের হিসাবে date রূপান্তর করে তারিখের পার্থক্য গণনা করতে পারেন as.Date date ব্যবহার করে এবং এটি একটি difftime সাথে তুলনা করে। number পার্থক্য একইভাবে করা হয়।

i1 <- seq_len(nrow(df1)) #Store all rows 
i2 <- seq_len(nrow(df2))
res <- do.call(rbind, sapply(seq_len(nrow(df1)), function(i) { #Loop over all rows in df1
  t1 <- which(df1$ctry[i] == df2$ctry) #Match ctry
  t2 <- grepl(df1$city[i], df2$city[t1]) | sapply(df2$city[t1], grepl, df1$city[i]) #Match city
  t1 <- t1[t2 & abs(as.Date(df1$date[i]) - as.Date(df2$date[t1[t2]])) <=
    as.difftime(threshold.date, units = "days") & #Test for date difference
    abs(df1$number[i] - df2$other_number[t1[t2]]) <= threshold.numbers] #Test for number difference
  if(length(t1) > 0) { #Match found
    i1 <<- i1[i1!=i] #Remove row as it was found
    i2 <<- i2[i2!=t1]
    cbind(df1[i,], df2[t1,c("other_number","other_col")], match=".") 
  }
}))
rbind(res
    , cbind(df1[i1,], other_number=NA, other_col=NA, match="1")
    , cbind(df2[i2,1:3], number=NA, col=NA, other_number=df2[i2,4]
            , other_col=df2[i2,5], match="2"))
#          date        city        ctry number      col other_number other_col match
#1   2003-08-29      Berlin     Germany     10    apple           13    yellow     .
#2   1999-06-12       Paris      France     20   banana           17     green     .
#6   1999-06-30  Copenhagen     Denmark     60 cucumber           61    orange     .
#3   2000-08-29      London          UK     30     pear           NA      <NA>     1
#4   1999-02-24        Rome       Italy     40   banana           NA      <NA>     1
#5   2001-04-17        Bern Switzerland     50    lemon           NA      <NA>     1
#7   1999-03-16      Warsaw      Poland     70    apple           NA      <NA>     1
#8   1999-07-16      Moscow      Russia     80    peach           NA      <NA>     1
#9   2001-08-29       Tunis     Tunisia     90   cherry           NA      <NA>     1
#10  2002-07-30      Vienna     Austria    100   cherry           NA      <NA>     1
#31  2000-08-29 near London          UK     NA     <NA>         3100      blue     2
#41  1999-02-24        Rome       Italy     NA     <NA>           45       red     2
#51  2001-04-17      Zurich Switzerland     NA     <NA>           51    purple     2
#71  1999-03-14      Warsaw      Poland     NA     <NA>          780      blue     2
#81  1999-07-17      Moscow      Russia     NA     <NA>           85       red     2
#91  2000-01-29       Tunis     Tunisia     NA     <NA>           90     black     2
#101 2002-07-01      Vienna     Austria     NA     <NA>          101     beige     2

আমি প্রথমে শহরের নামগুলি চরিত্রের ভেক্টরগুলিতে পরিণত করেছি, যেহেতু (যদি আমি সঠিকভাবে বুঝতে পারি) তবে আপনি df2 এর মধ্যে থাকা শহরের নাম অন্তর্ভুক্ত করতে চান।

df1$city<-as.character(df1$city)
df2$city<-as.character(df2$city)

তারপরে তাদের দেশটিতে মার্জ করুন:

df = merge(df1, df2, by = ("ctry"))

> df
          ctry     date.x     city.x number      col     date.y      city.y other_number other_col
1      Austria 2002-07-30     Vienna    100   cherry 2002-07-01      Vienna          101     beige
2      Denmark 1999-06-30 Copenhagen     60 cucumber 1999-06-29  Copenhagen           61    orange
3       France 1999-06-12      Paris     20   banana 1999-06-12  East-Paris           17     green
4      Germany 2003-08-29     Berlin     10    apple 2003-08-29      Berlin           13    yellow
5        Italy 1999-02-24       Rome     40   banana 1999-02-24        Rome           45       red
6       Poland 1999-03-16     Warsaw     70    apple 1999-03-14      Warsaw          780      blue
7       Russia 1999-07-16     Moscow     80    peach 1999-07-17      Moscow           85       red
8  Switzerland 2001-04-17       Bern     50    lemon 2001-04-17      Zurich           51    purple
9      Tunisia 2001-08-29      Tunis     90   cherry 2000-01-29       Tunis           90     black
10          UK 2000-08-29     London     30     pear 2000-08-29 near London         3100      blue

লাইব্রেরি stringr আপনাকে ਸ਼ਹਿਰ.ইক্সের মধ্যে রয়েছে কিনা তা দেখতে দেয় y এখানে (শেষ কলামটি দেখুন):

library(stringr)
df$city_keep<-str_detect(df$city.y,df$city.x) # this returns logical vector if city.x is contained in city.y (works one way)
> df
          ctry     date.x     city.x number      col     date.y      city.y other_number other_col city_keep
1      Austria 2002-07-30     Vienna    100   cherry 2002-07-01      Vienna          101     beige      TRUE
2      Denmark 1999-06-30 Copenhagen     60 cucumber 1999-06-29  Copenhagen           61    orange      TRUE
3       France 1999-06-12      Paris     20   banana 1999-06-12  East-Paris           17     green      TRUE
4      Germany 2003-08-29     Berlin     10    apple 2003-08-29      Berlin           13    yellow      TRUE
5        Italy 1999-02-24       Rome     40   banana 1999-02-24        Rome           45       red      TRUE
6       Poland 1999-03-16     Warsaw     70    apple 1999-03-14      Warsaw          780      blue      TRUE
7       Russia 1999-07-16     Moscow     80    peach 1999-07-17      Moscow           85       red      TRUE
8  Switzerland 2001-04-17       Bern     50    lemon 2001-04-17      Zurich           51    purple     FALSE
9      Tunisia 2001-08-29      Tunis     90   cherry 2000-01-29       Tunis           90     black      TRUE
10          UK 2000-08-29     London     30     pear 2000-08-29 near London         3100      blue      TRUE

তারপরে আপনি তারিখের মধ্যে দিনের মধ্যে পার্থক্য পেতে পারেন:

df$dayDiff<-abs(as.POSIXlt(df$date.x)$yday - as.POSIXlt(df$date.y)$yday)

এবং সংখ্যার পার্থক্য:

df$numDiff<-abs(df$number - df$other_number)

ফলাফল ডেটা ফ্রেমের মতো দেখতে এখানে ছিল:

> df
          ctry     date.x     city.x number      col     date.y      city.y other_number other_col city_keep dayDiff numDiff
1      Austria 2002-07-30     Vienna    100   cherry 2002-07-01      Vienna          101     beige      TRUE      29       1
2      Denmark 1999-06-30 Copenhagen     60 cucumber 1999-06-29  Copenhagen           61    orange      TRUE       1       1
3       France 1999-06-12      Paris     20   banana 1999-06-12  East-Paris           17     green      TRUE       0       3
4      Germany 2003-08-29     Berlin     10    apple 2003-08-29      Berlin           13    yellow      TRUE       0       3
5        Italy 1999-02-24       Rome     40   banana 1999-02-24        Rome           45       red      TRUE       0       5
6       Poland 1999-03-16     Warsaw     70    apple 1999-03-14      Warsaw          780      blue      TRUE       2     710
7       Russia 1999-07-16     Moscow     80    peach 1999-07-17      Moscow           85       red      TRUE       1       5
8  Switzerland 2001-04-17       Bern     50    lemon 2001-04-17      Zurich           51    purple     FALSE       0       1
9      Tunisia 2001-08-29      Tunis     90   cherry 2000-01-29       Tunis           90     black      TRUE     212       0
10          UK 2000-08-29     London     30     pear 2000-08-29 near London         3100      blue      TRUE       0    3070

তবে আমরা এমন জিনিসগুলি ফেলে দিতে চাই যেখানে সিটি.এক্স সিটির মধ্যে পাওয়া যায় নি, যেখানে দিনের পার্থক্য 5 এর চেয়ে বেশি বা সংখ্যার পার্থক্য 3 এর চেয়ে বেশি:

df<-df[df$dayDiff<=5 & df$numDiff<=3 & df$city_keep==TRUE,]

> df
     ctry     date.x     city.x number      col     date.y     city.y other_number other_col city_keep dayDiff numDiff
2 Denmark 1999-06-30 Copenhagen     60 cucumber 1999-06-29 Copenhagen           61    orange      TRUE       1       1
3  France 1999-06-12      Paris     20   banana 1999-06-12 East-Paris           17     green      TRUE       0       3
4 Germany 2003-08-29     Berlin     10    apple 2003-08-29     Berlin           13    yellow      TRUE       0       3

আপনার উপরের তিনটি সারি যা অবশিষ্ট রয়েছে তা (যা কলাম 1 এ বিন্দু রয়েছে)।

এখন আমরা তৈরি করা তিনটি কলাম এবং df2 থেকে তারিখ এবং শহরটি ফেলে দিতে পারি:

> df<-subset(df, select=-c(city.y, date.y, city_keep, dayDiff, numDiff))
> df
     ctry     date.x     city.x number      col other_number other_col
2 Denmark 1999-06-30 Copenhagen     60 cucumber           61    orange
3  France 1999-06-12      Paris     20   banana           17     green
4 Germany 2003-08-29     Berlin     10    apple           13    yellow

এখানে একটি নমনীয় পন্থা যা আপনাকে চয়ন করা মার্জ মানদণ্ডের যে কোনও সংগ্রহ নির্দিষ্ট করতে দেয়।

প্রস্তুতি কাজ

আমি নিশ্চিত করেছি যে df1 এবং df2 সমস্ত স্ট্রিংগুলি স্ট্রিং ছিল, কারণ নয় (অন্যান্য উত্তরের বেশ কয়েকটি হিসাবে উল্লেখ করা হয়েছে)। আমি তারিখগুলি আসল তারিখগুলিতে তৈরি করে as.Date

সংযুক্তির মানদণ্ড উল্লেখ করুন

তালিকার একটি তালিকা তৈরি করুন। মূল তালিকার প্রতিটি উপাদান একটি মানদণ্ড; একটি মানদণ্ডের সদস্যরা হলেন

  • final.col.name : চূড়ান্ত সারণিতে আমরা যে কলামটির নাম final.col.name তার নাম
  • col.name.1 : col.name.1 এ কলামের নাম
  • col.name.2 : col.name.2 এ কলামের নাম
  • exact : বুলিয়ান; আমাদের এই কলামে সঠিক মিল থাকা উচিত?
  • threshold : থ্রেশহোল্ড (আমরা সঠিক মিল না নিলে)
  • match.function ফাংশন: একটি ফাংশন যা সারিগুলি মেলে কিনা তা ফেরত দেয় (বিশেষ ক্ষেত্রে যেমন স্ট্রিং মেলানোর জন্য grepl ব্যবহার করা; নোট করুন যে এই ফাংশনটি অবশ্যই ভেক্টরাইজড থাকতে হবে)
merge.criteria = list(
  list(final.col.name = "date",
       col.name.1 = "date",
       col.name.2 = "date",
       exact = F,
       threshold = 5),
  list(final.col.name = "city",
       col.name.1 = "city",
       col.name.2 = "city",
       exact = F,
       match.function = function(x, y) {
         return(mapply(grepl, x, y) |
                  mapply(grepl, y, x))
       }),
  list(final.col.name = "ctry",
       col.name.1 = "ctry",
       col.name.2 = "ctry",
       exact = T),
  list(final.col.name = "number",
       col.name.1 = "number",
       col.name.2 = "other_number",
       exact = F,
       threshold = 3)
)

মার্জ করার জন্য ফাংশন

এই ফাংশনটিতে তিনটি আর্গুমেন্ট লাগে: দুটি ডেটা ফ্রেম আমরা মার্জ করতে চাই এবং ম্যাচের মানদণ্ডের তালিকা। এটি নিম্নলিখিত হিসাবে এগিয়ে যায়:

  1. ম্যাচের মানদণ্ডের মধ্য দিয়ে আইট্রেট করুন এবং নির্ধারণ করুন যে কোন সারির জোড়গুলি সমস্ত মানদণ্ড পূরণ করে না don't (@ জি.কি. এর উত্তরে অনুপ্রাণিত হয়ে, এটি সম্পূর্ণ বাইরের যোগদানের পরিবর্তে সারি সূচকগুলি ব্যবহার করে, এটি বড় ডেটাসেটের জন্য কম স্মৃতিশক্তিযুক্ত হতে পারে))
  2. আমরা চাই কেবল সারিগুলির সাথে একটি কঙ্কাল ডেটা ফ্রেম তৈরি করুন (ম্যাচের ক্ষেত্রে সারিগুলিকে একীভূত করা হবে, তুলনাহীন রেকর্ডগুলির জন্য সজ্জিত সারিগুলি নেই)।
  3. মূল ডেটা ফ্রেমের কলামগুলির মধ্য দিয়ে ইটারেট করুন এবং এগুলি নতুন ডেটা ফ্রেমে কাঙ্ক্ষিত কলামগুলি পপুলেট করতে ব্যবহার করুন। (ম্যাচের মানদণ্ডে প্রদর্শিত কলামগুলির জন্য প্রথমে এটি করুন এবং তারপরে বাকি যে কোনও কলামের জন্য রেখে দিন))
library(dplyr)
merge.data.frames = function(df1, df2, merge.criteria) {
  # Create a data frame with all possible pairs of rows from df1 and rows from
  # df2.
  row.decisions = expand.grid(df1.row = 1:nrow(df1), df2.row = 1:nrow(df2))
  # Iterate over the criteria in merge.criteria.  For each criterion, flag row
  # pairs that don't meet the criterion.
  row.decisions$merge = T
  for(criterion in merge.criteria) {
    # If we're looking for an exact match, test for equality.
    if(criterion$exact) {
      row.decisions$merge = row.decisions$merge &
        df1[row.decisions$df1.row,criterion$col.name.1] == df2[row.decisions$df2.row,criterion$col.name.2]
    }
    # If we're doing a threshhold test, test for difference.
    else if(!is.null(criterion$threshold)) {
      row.decisions$merge = row.decisions$merge &
        abs(df1[row.decisions$df1.row,criterion$col.name.1] - df2[row.decisions$df2.row,criterion$col.name.2]) <= criterion$threshold
    }
    # If the user provided a function, use that.
    else if(!is.null(criterion$match.function)) {
      row.decisions$merge = row.decisions$merge &
        criterion$match.function(df1[row.decisions$df1.row,criterion$col.name.1],
                                 df2[row.decisions$df2.row,criterion$col.name.2])
    }
  }
  # Create the new dataframe.  Just row numbers of the source dfs to start.
  new.df = bind_rows(
    # Merged rows.
    row.decisions %>% filter(merge) %>% select(-merge),
    # Rows from df1 only.
    row.decisions %>% group_by(df1.row) %>% summarize(matches = sum(merge)) %>% filter(matches == 0) %>% select(df1.row),
    # Rows from df2 only.
    row.decisions %>% group_by(df2.row) %>% summarize(matches = sum(merge)) %>% filter(matches == 0) %>% select(df2.row)
  )
  # Iterate over the merge criteria and add columns that were used for matching
  # (from df1 if available; otherwise from df2).
  for(criterion in merge.criteria) {
    new.df[criterion$final.col.name] = coalesce(df1[new.df$df1.row,criterion$col.name.1],
                                                df2[new.df$df2.row,criterion$col.name.2])
  }
  # Now add all the columns from either data frame that weren't used for
  # matching.
  for(other.col in setdiff(colnames(df1),
                           sapply(merge.criteria, function(x) x$col.name.1))) {
    new.df[other.col] = df1[new.df$df1.row,other.col]
  }
  for(other.col in setdiff(colnames(df2),
                           sapply(merge.criteria, function(x) x$col.name.2))) {
    new.df[other.col] = df2[new.df$df2.row,other.col]
  }
  # Return the result.
  return(new.df)
}

ফাংশন প্রয়োগ করুন, এবং আমরা সম্পন্ন করেছি

df = merge.data.frames(df1, df2, merge.criteria)

পদক্ষেপ 1: "শহর" এবং "ক্রিট" এর উপর ভিত্তি করে ডেটা মার্জ করুন:

df = merge(df1, df2, by = c("city", "ctry"))

পদক্ষেপ 2: তারিখের এন্ট্রি> থ্রেশোল্ড.ডেট (দিনের মধ্যে) এর মধ্যে পার্থক্য থাকলে সারিগুলি সরিয়ে ফেলুন:

date_diff = abs(as.numeric(difftime(strptime(df$date.x, format = "%Y-%m-%d"),
                                    strptime(df$date.y, format = "%Y-%m-%d"), units="days")))
index_remove = date_diff > threshold.date
df = df[-index_remove,]

পদক্ষেপ 3: সংখ্যার মধ্যে পার্থক্য> থ্রেশহোল্ড.নম্বার থাকলে সারিগুলি সরান:

number_diff = abs(df$number - df$other_number) 
index_remove = number_diff > threshold.numbers
df = df[-index_remove,]

শর্তগুলি প্রয়োগ না করার ক্ষেত্রে শর্ত প্রয়োগের আগে ডেটা একত্রীকরণ করা উচিত।





dataframe