Эффективный способ определения класса с несколькими, необязательно-пустыми слотами в S4 R?

StackOverflow https://stackoverflow.com/questions/8334796

  •  26-10-2019
  •  | 
  •  

Вопрос

Я строю пакет для обработки данных, которые прибывают с 4 различными типами. Каждый из этих типов является законным классом в форме матрицы, data.frame или дерева. В зависимости от того, как обрабатываются данные, и другие экспериментальные факторы, некоторые из этих компонентов данных могут отсутствовать, но все еще чрезвычайно полезно иметь возможность хранить эту информацию в качестве экземпляра специального класса и иметь методы, которые распознают различный компонент данные.

Подход 1:

Я экспериментировал с постепенной структурой наследования, которая выглядит как вложенное дерево, где каждая комбинация типов данных имеет свой собственный класс явно определен. Это, кажется, трудно расширить для дополнительных типов данных в будущем, а также трудно для новых разработчиков, чтобы изучить все имена классов, какими бы хорошо организовались эти имена.

Подход 2:

Вторым подходом является создание одного «мастер-класса», который включает в себя слот для всех 4 типов данных. Чтобы позволить слотам быть нулевыми для экземпляров отсутствующих данных, представляется необходимым сначала определить союз виртуального класса между NULL Класс и новый класс типа данных, а затем используйте союз виртуального класса в качестве ожидаемого класса для соответствующего слота в мастер-классе. Вот пример (при условии, что каждый класс типа данных уже определен):

################################################################################
# Use setClassUnion to define the unholy NULL-data union as a virtual class.
################################################################################    
setClassUnion("dataClass1OrNULL", c("dataClass1", "NULL"))
setClassUnion("dataClass2OrNULL", c("dataClass2", "NULL"))
setClassUnion("dataClass3OrNULL", c("dataClass3", "NULL"))
setClassUnion("dataClass4OrNULL", c("dataClass4", "NULL"))
################################################################################
# Now define the master class with all 4 slots, and 
# also the possibility of empty (NULL) slots and an explicity prototype for
# slots to be set to NULL if they are not provided at instantiation.
################################################################################
setClass(Class="theMasterClass", 
    representation=representation(
        slot1="dataClass1OrNULL",
        slot2="dataClass2OrNULL",
        slot3="dataClass3OrNULL",
        slot4="dataClass4OrNULL"),
    prototype=prototype(slot1=NULL, slot2=NULL, slot3=NULL, slot4=NULL)
)
################################################################################

Таким образом, вопрос может быть перефразирован как:

Есть ли более эффективные и/или гибкие альтернативы любому из этих подходов?

Этот пример изменен из ответа на Так что вопрос о установке значения слота по умолчанию в NULL. Анкет Этот вопрос отличается тем, что я заинтересован в том, чтобы узнать лучшие варианты в R для создания классов с слотами, которые могут быть пустыми, если это необходимо, несмотря на то, что требуется конкретный сложный класс во всех других непустых случаях.

Это было полезно?

Решение

По моему мнению...

Подход 2

Это как бы побеждает цель принять формальную систему классов, а затем создать класс, который содержит плохо определенные слоты («A» или NULL). Как минимум, я бы попытался сделать DataClass1, иметь нулевый дефолт. В качестве простого примера, по умолчанию здесь является нулевая цифровая вектор.

setClass("DataClass1", representation=representation(x="numeric"))
DataClass1 <- function(x=numeric(), ...) {
    new("DataClass1", x=x, ...)
}

затем

setClass("MasterClass1", representation=representation(dataClass1="DataClass1"))
MasterClass1 <- function(dataClass1=DataClass1(), ...) {
    new("MasterClass1", dataClass1=dataClass1, ...)
}

Одним из преимуществ этого является то, что методы не должны проверять, является ли экземпляр в слоте NULL или DataClass1 '

setMethod(length, "DataClass1", function(x) length(x@x))
setMethod(length, "MasterClass1", function(x) length(x@dataClass1))

> length(MasterClass1())
[1] 0
> length(MasterClass1(DataClass1(1:5)))
[1] 5

В ответ на ваш комментарий о предупреждающих пользователях, когда они получают доступ к «пустым» слотам, и вспоминают, что пользователи обычно хотят, чтобы функции делали что -то, а не говорят им, что они делают что -то не так, я бы, вероятно, вернул пустой объект DataClass1() которые точно отражают состояние объекта. Может быть, а show Метод предоставил бы обзор, который усилил статус слота - DataClass1: Нет. Это кажется особенно уместным, если MasterClass1 представляет собой способ координации нескольких различных анализов, из которых пользователь может сделать только некоторые.

Ограничение этого подхода (или ваш подход 2) состоит в том, что вы не получаете отправку метода - вы не можете писать методы, которые подходят только для экземпляра с DataClass1 случаи, которые имеют ненулевую длину и вынуждены делать какую-то ручную отправку (например, с if или же switch) Это может показаться ограничением для разработчика, но это также применимо к пользователю-пользователь не понимает, какие операции уникально подходят для экземпляров MasterClass1, которые имеют ненулевую длину экземпляры DataClass1.

Подход 1

Когда вы говорите, что имена классов в иерархии будут сбивают с толку вашего пользователя, кажется, что это, возможно, указывает на более фундаментальную проблему - вы слишком стараетесь сделать всеобъемлющее представление типов данных; Пользователь никогда не сможет отслеживать ClassWithMatrixDataFrameAndtree, потому что он не представляет способ просмотра данных. Возможно, это возможность сократить ваши амбиции, чтобы действительно заняться только самыми выдающимися частями области, которую вы расследуете. Или, возможно, возможность переосмыслить, как пользователь может подумать и взаимодействовать с собранными данными, и использовать отделение интерфейса (что пользователь видит) от реализации (как вы решили представлять данные в классы) предоставлены системами классов, чтобы более эффективно инкапсулировать то, что может делать пользователь.

Откладывая именование и количество классов в стороне, когда вы говорите «трудно расширить для дополнительных типов данных в будущем», это заставляет меня задуматься, может ли некоторые из нюансов классов S4 сбивают вас с толку? Короткое решение состоит в том, чтобы не писать свой собственный initialize методы и полагаются на конструкторы для выполнения сложной работы в соответствии с

setClass("A", representation(x="numeric"))
setClass("B", representation(y="numeric"), contains="A")

A <- function(x = numeric(), ...) new("A", x=x, ...)
B <- function(a = A(), y = numeric(), ...) new("B", a, y=y, ...)

а потом

> B(A(1:5), 10)
An object of class "B"
Slot "y":
[1] 10

Slot "x":
[1] 1 2 3 4 5
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top