3

ある列にカテゴリラベルがあり、別の列に一致する用語/パターンがある別のデータフレームに基づいて分類する必要がある文字列の列を持つデータフレームがあります。

50 以上のカテゴリがあり、各文字列は複数のカテゴリに一致する可能性がありますが、他のカテゴリには一致しません。これらの文字列にカテゴリ ラベルを効率的にタグ付けするにはどうすればよいですか?

以下は、簡単なサンプル データセットと、取得したい出力です。違いがあるとすれば、実際のデータ セット内の文字列はこれらのサンプル文字列よりもはるかに長く、その数は数十万になります。

recipes <- c('fresh asparagus', 'a bunch of bananas', 'one pound pork', 'no fruits, no veggies, no nothing', 'broccoli or spinach','I like apples, asparagus, and pork', 'meats like lamb', 'venison sausage and fried eggs', 'spinach and arugula salad', 'scrambled or poached eggs', 'sourdough english muffins')
recipes_df <- data.frame(recipes, stringsAsFactors = FALSE)

category <- c('vegetable', 'fruit', 'meat','bread','dairy')
items <- c('arugula|asparagus|broccoli|peas|spinach', 'apples|bananas|blueberries|oranges', 'lamb|pork|turkey|venison', 'sourdough', 'buttermilk|butter|cream|eggs')
category_df <- data.frame(category, items)

これは私が得たいと思っている出力です:

                          recipes            recipes_category
1                     fresh asparagus              vegetable
2                  a bunch of bananas                  fruit
3                      one pound pork                   meat
4   no fruits, no veggies, no nothing                   <NA>
5                 broccoli or spinach              vegetable
6  I like apples, asparagus, and pork fruit, vegetable, meat
7                     meats like lamb                   meat
8      venison sausage and fried eggs            meat, dairy
9           spinach and arugula salad              vegetable
10          scrambled or poached eggs                  dairy
11          sourdough english muffins                 breads

grepl と for ループまたは apply のバージョンの組み合わせが必要だと思いますが、以下で試した例では、R をほとんど理解していないことがよくわかります。たとえば、sapply を使用すると期待どおりの結果が得られますが、よくわかりsapply(category_df$items, grepl, recipes_df$recipes)ませんこれらの結果を必要な単純な列に変換する方法。

ここにある categorize 関数を使用すると、各文字列に対して 1 つのカテゴリのみが一致します。

categorize_food <- function(df, searchString, category) {
  df$category <- "OTHER"
  for(i in seq_along(searchString)) {
    list <- grep(searchString[i], df[,1], ignore.case=TRUE) 
    if (length(list) > 0) {
  df$category[list] <- category[i]
    }
  }
  df
}
recipes_cat <- categorize_food(recipes_df, category_df$items, category_df$category)

同様に、ここで見つかった関数は私が探しているものに最も近いものですが、カテゴリ番号がそのようにマッピングされる理由がわかりません. 野菜のカテゴリは 2 ではなく 1、乳製品は 3 ではなく 5 になると思います。

vec = category_df$items
recipes_df$category = apply(recipes_df, 1, function(u){
  bool = sapply(vec, function(x) grepl(x, u[['recipes']]))
  if(any(bool)) vec[bool] else NA
})
4

2 に答える 2

1

最後の集計は大規模なデータセットの場合は少し遅いため、行を文字列に変換するより高速な方法 (data.table?) を検索することもできますが、これは一般的に機能するはずです。

tmplist <- strsplit(items, "|", fixed=TRUE)
#Removes horrid '|' separated values into neat rows
searchterms <- data.frame(category=rep(category, sapply(tmplist, length)),
           items=unlist(tmplist), stringsAsFactors=FALSE)
#Recreates data frame, neatly
res <- lapply(searchterms$items, grep, x=recipes, value=TRUE)
#throws an lapply on the neat data pattern against recipes

matched_times <- sapply(res, length)
df_matched <- data.frame( category = rep(searchterms$category[matched_times!=0],
                                 matched_times[matched_times != 0]),
                  recipes = unlist(res))
# Combines category names the correct nr of times with grep
#results (recipe names), to create a tidy result 

df_ummatched <- data.frame( category = NA, recipes = recipes[!recipes %in% unlist(res)])
df <- rbind(df_matched, df_ummatched)
#gets the nonmatched, plops it in with NA values. 

final  <- aggregate(category~recipes, data=df, paste, sep=",", na.action=na.pass)
#makes the data untidy, as you asked. 

しかし、これでも重複したvegetable, vegetableエントリが残ります。それはありません:

SplitFunction <- function(x) {
  b <- unlist(strsplit(x, ','))
  c <- b[!duplicated(b)]
  return(paste(c, collapse=", "))
}
SplitFunctionV <- Vectorize(SplitFunction)
final$category <- SplitFunctionV(final$category)

そして結果:

final
                              recipes               category
1                  a bunch of bananas                  fruit
2                 broccoli or spinach              vegetable
3                     fresh asparagus              vegetable
4  I like apples, asparagus, and pork vegetable, fruit, meat
5                     meats like lamb                   meat
6                      one pound pork                   meat
7           scrambled or poached eggs                  dairy
8           sourdough english muffins                  bread
9           spinach and arugula salad              vegetable
10     venison sausage and fried eggs            meat, dairy
11  no fruits, no veggies, no nothing                     NA
于 2015-11-12T16:11:42.780 に答える