#r #performance #optimization #twitter #anonymize
Вопрос:
Я написал функцию для анонимизации имен в фрейме данных с заданным ключом, и она обходит, как только доходит до анонимизации очень многих имен, но я не понимаю, почему.
Рассматриваемый фрейм данных представляет собой набор из 4733 твитов, собранных через API Twitter, где каждая строка представляет собой твит с 32 столбцами данных. Имена должны быть анонимизированы независимо от того, в какой строке они отображаются, поэтому я бы не хотел ограничивать функцию просмотром только пары из этих 32 столбцов.
Ключ-это фрейм данных, содержащий 211121 пару реальных и поддельных имен, как реальных, так и поддельных, уникальных в фрейме данных. Функция сильно замедляется после анонимизации около 100 тысяч имен.
Функция выглядит следующим образом:
pseudonymize <- function(df, key) {
for(name in key$realNames) {
df <- as.data.frame(apply(df, 2, function(column) gsub(name, key[key$realNames == name, 2], column)))
}
}
Есть ли здесь какая-то очевидная вещь, которая могла бы вызвать замедление? У меня совсем нет опыта в оптимизации кода для скорости.
ПРАВКА1:
Вот несколько строк из фрейма данных, подлежащих анонимизации.
"https://twitter.com/__jgil/statuses/825559753447313408","__jgil",0.000576911235261567,756,4,13,17,7,16,23,10,0.28166915052161,0.390123456790124,0.00271311644806025,0.474529795261862,0.00641025649383664,"@jadahung20 GIRL I am tooooooo salty tonight lolll","lolll","adjoint","anglais","indefini","anglais","anglais","non","iPhone, Twitter",4057,214,241,"Canada","Nouvelle-Ecosse","Middleton","indefini","Shari"
"https://twitter.com/__paigewhite/statuses/827988259573788673","__paigewhite",0,1917,0,8,8,0,9,9,16,0.143476044852192,0.162056634159209,0.000172947386274259,0,0,"@abbytutty_ i miss emily lololol _Ù÷â_Ù÷É","lololol","adjoint","anglais","indefini","anglais","anglais","non","iPhone, Twitter",8366,392,661,"Canada","Nouvelle-Ecosse","indefini","indefini","Shari"
"https://twitter.com/_brookehynes/statuses/821022926287884288","_brookehynes",0,1917,1,6,7,1,7,8,1,1,1,0.000196850793912616,0.00393656926735126,0.200000002980232,"@tdesj3 @belle lol yea doubt it.","lol","adjoint","indefini","anglais","anglais","anglais","non","iPhone, Twitter",1184,87,70,"Canada","Nouvelle-Ecosse","Halifax","indefini","Shari"
Вот несколько строк из ключа.
"","realNames","fakeNames"
"1","________","Tajid_Pinkley"
"2","____________aho","Monica_Yujiri"
"3","___________ass","Alexander_Garay-Grajeda"
ПРАВКА2:
Я упростил DF только до двух столбцов, которые требовали бы анонимизации, и это значительно ускорило процесс, но он все равно выходит после того, как было сделано около 155 тысяч имен.
Как указано в комментариях, вот dput()
выходные данные для первых трех строк DF, которые должны быть анонимизированы.
structure(list(
utilisateur = c("___Yeliab", "__courtlezz", "__courtlezz"),
texte = c("@EmilyIsPro ik lol", "@NikkiErica21 there was a sighting in sunset ridge too. Keep Winnie and bob safe lol", "@NikkiErica21 lol yes _Ã231։")
),
row.names = c(NA, 3L),
class = "data.frame")
А вот dput()
для первых трех строк ключа.
structure(list(
realNames = c("________", "____________aho", "___________ass"),
fakeNames = c("Abhinav_Chang", "Caleb_Dunn-Sparks", "Taryn_Hunzicker")
),
row.names = c(NA, 3L),
class = "data.frame")
Комментарии:
1. Пожалуйста, поделитесь небольшим, воспроизводимым (копируемым/вставляемым!) образцом ввода.
2. Трудно сказать, не видя ваших структур данных, но вы выполняете много преобразований внутри цикла.
apply
преобразует фреймы данных в матрицы — вам, вероятно, вообще не следует их использовать.as.data.frame
преобразуется обратно в фрейм данных. Действительно ли вам нужно преобразовывать свой объект в матрицу, а затем обратно в фрейм данных на каждой итерации? Если вы сможете перенести эти операции за пределы цикла-преобразовать все один раз-это будет происходить быстрее. И когда мы увидим входные данные, вам, возможно, вообще не понадобятся преобразования.3. Кроме того, если вы не используете специальные символы регулярных выражений, использование
fixed = TRUE
аргумента будетgsub()
намного быстрее. И могут быть варианты векторизации, так что вам вообще не нужен цикл…4. Не могли бы вы поделиться данными,
dput()
чтобы включить всю информацию о классе и структуре?dput(df[1:3, ])
иdput(key([1:3])
было бы здорово.
Ответ №1:
Воздействие на данные в виде вектора, а не фрейма данных, будет намного эффективнее. Я столкнулся с некоторыми проблемами с кодировкой, поэтому преобразовал текст в UTF-8 с помощью iconv
; Если имена содержат символы, отличные от ASCII, это потребует некоторой обработки.
key1 <- data.frame(
realNames = c("________", "____________aho", "___________ass",
"___Yeliab", "__courtlezz", "NikkiErica21", "EmilyIsPro", "aho"),
fakeNames = c("Abhinav_Chang", "Caleb_Dunn-Sparks", "Taryn_Hunzicker",
"A_A", "B_B", "C_C", "D_D", "E_E"),
stringsAsFactors = FALSE
)
pseudonymize1 <- function(df, key) {
mat <- as.matrix(df)
dims <- attr(mat, which = "dim")
cnam <- colnames(df)
vec <- iconv(unclass(mat), from = "latin1", to = "UTF-8")
for (name in split(key, f = seq_len(nrow(key)))) {
vec <- gsub(
vec,
pattern = name$realNames,
replacement = name$fakeNames,
fixed = TRUE)
}
mat <- vec
attr(mat, which = "dim") <- dims
df <- as.data.frame(mat, stringsAsFactors = FALSE)
colnames(df) <- cnam
df
}
pseudonymize1(df1, key1)
# utilisateur texte
# 1 A_A @D_D ik lol
# 2 B_B @C_C there was a sighting in sunset ridge too. Keep Winnie and bob safe lol
# 3 B_B @C_C lol yes _Ãu0083u0099Ãu0083·Ãu0083¢
library(microbenchmark)
microbenchmark(
pseudonymize(df1, key1),
pseudonymize1(df1, key1)
)
# Unit: microseconds
# expr min lq mean median uq max neval cld
# pseudonymize(df1, key1) 1842.554 1885.6750 2131.089 1994.755 2294.6850 3007.371 100 b
# pseudonymize1(df1, key1) 287.683 306.1905 333.678 314.950 339.8705 497.301 100 a
Меня беспокоит, что 155 тысяч имен связаны с тем, что при поиске в виде регулярного выражения вы найдете имена, содержащиеся в других именах. Это может быть истинное имя в пределах истинного имени (например, Эмили в EmilyIsPro) или истинное имя в пределах ранее замененного поддельного имени. Вы захотите проверить это и рассмотреть возможность использования случайного хэша вместо поддельного имени, подобного имени.