Путь Ruby заключается в том, чтобы подделать его с запутанными хаками, которые заставят некоторых пользователей задаться вопросом: «Как в fuck это даже работает?», в то время как менее любопытный просто запоминает синтаксис, необходимый для использования этой вещи. Если вы когда-либо использовали Rake или Rails, вы видели такие вещи.
Вот такой хак:
def mlet(name,func)
my_class = (Class.new do
def initialize(name,func)
@name=name
@func=func
end
def method_missing(methname, *args)
puts "method_missing called on #{methname}"
if methname == @name
puts "Calling function #{@func}"
@func.call(*args)
else
raise NoMethodError.new "Undefined method `#{methname}' in mlet"
end
end
end)
yield my_class.new(name,func)
end
Что это значит, который создает класс и передает его блоку. Класс использует method_missing
, чтобы притворяться, что у него есть метод с именем, которое вы выбрали. Он «реализует» метод, вызывая лямбда, которую вы должны предоставить. Назвав объект однобуквенным именем, вы можете свести к минимуму количество лишнего ввода, которое оно требует (что то же самое, что Rails делает в schema.rb
). mlet
назван в честь формы Common Lisp flet
, за исключением случаев, когда f
обозначает «функцию», m
обозначает «метод».
Вы используете его следующим образом:
def outer
mlet :inner, ->(x) { x*2 } do |c|
c.inner 12
end
end
Можно создать аналогичное приспособление, которое позволяет определять множество внутренних функций без дополнительного вложения, но для этого требуется еще более уродливый взлом такого рода, который вы можете найти в реализации Rake или Rspec. Выяснение того, как работает Rspec let!
, даст вам долгий путь к созданию такой ужасной мерзости.
Использование data.table
:
library(data.table)
idt <- as.data.table(iris)
idt[, .SD[11:(.N-10)], Species]
Та же логика в base R
:
do.call(
rbind,
lapply(
split(iris, iris[["Species"]]),
function(x) x[11:(nrow(x)-10), ]
)
)
Здесь решение с dplyr
.
В моем примере я вырезал только первое и последнее значения. (вы можете изменить его, изменив 2 на любое число в filter
).
Идея состоит в том, чтобы добавить после group_by
id номер строки для каждого наблюдения, начиная сверху (n
) и наоборот снизу (n1
), тогда вы просто отфильтруете. [119 ]
library(dplyr)
data %>%
group_by(id) %>%
mutate(n=1:n(),
n1 = n():1) %>% # n and n1 are the row numbers
filter(n >= 2,n1 >= 2) %>% # change 2 with 10, or whatever
# filter() keeps only the rows that you want
select(-n, -n1) %>%
ungroup()
# # A tibble: 4 x 2
# id value
# <dbl> <int>
# 1 1 6
# 2 1 8
# 3 2 1
# 4 2 2
Данные:
set.seed(123)
data <- data.frame(id = c(rep(1,4), rep(2,4)), value=sample(8))
data
# id value
# 1 1 3
# 2 1 6
# 3 1 8
# 4 1 5
# 5 2 4
# 6 2 1
# 7 2 2
# 8 2 7
Если вы знаете, что у вас всегда есть более 20 точек данных по корове, вы можете сделать следующее, как показано в наборе данных iris
:
library(dplyr)
dim(iris)
# [1] 150 5
iris_trimmed <-
iris %>%
group_by(Species) %>%
slice(11:(n()-10)) %>%
ungroup()
dim(iris_trimmed)
# [1] 90 5
По вашим данным:
res <-
your_data %>%
group_by(Cow) %>%
slice(11:(n()-10)) %>%
ungroup()
[116 ] В базе R вы можете сделать:
iris_trimmed <- do.call(
rbind,
lapply(split(iris, iris$Species),
function(x) head(tail(x,-10),-10)))
dim(iris_trimmed)
# [1] 90 5