Sorting List of List of Elements of a Custom Class in R

Sorting list of list of elements of a custom class in R?

This answer from Aaron demonstrates, exactly, what is needed to apply a customized sort on a classed object. As Roland notes, you -actually- need to sort "L" and, thus, that is where the focus on custom sort should be. To provide flexibility specifying on which index of "L" 's elements to sort, a way would be to store an extra attr on "L":

Turn "L" to an appropriate object:

class(L) = "myclass"
attr(L, "sort_ind") = 1L

Ops methods need to be defined (extract the relevant element of your data):

"<.myclass" = function(x, y) 
{
i = attr(x, "sort_ind") ## also check if 'x' and 'y' have the same 'attr(, "sort_ind")'
x[[1]][[i]] < y[[1]][[i]]
}
"==.myclass" = function(x, y)
{
i = attr(x, "sort_ind")
x[[1]][[i]] == y[[1]][[i]]
}
">.myclass" = function(x, y)
{
i = attr(x, "sort_ind")
x[[1]][[i]] > y[[1]][[i]]
}

And a subset method:

"[.myclass" = function(x, i) 
{
y = .subset(x, i)
attributes(y) = attributes(x)
return(y)
}

The above methods are necessary (perhaps, except "<") to be defined since a call to sort/order will end up calling rank which needs .gt in order to subset accordingly each element and compare.
Finally, a get/set function for sauce:

sort_ind = function(x) attr(x, "sort_ind")
"sort_ind<-" = function(x, value)
{
attr(x, "sort_ind") = value
return(x)
}

And:

order(L)
#[1] 3 2 1
sort_ind(L) = 3
order(L)
#[1] 2 3 1

A method for sort can be, also, created to wrap all the above:

sort.myclass = function(x, sort_ind = attr(x, "sort_ind"), ...)
{
sort_ind(x) = sort_ind
NextMethod()
}

sort(L)
sort(L, sort_ind = 1)

(I assumed that your toList function would look like something toList = function(x) x[[1L]])

Sort a list of nontrivial elements in R

To make this is as simple I can, say your objects are lists with two elements, a name and a value. The value is a numeric; that's what we want to sort by. You can imagine having more elements and needing to do something more complex to sort.

The sort help page tells us that sort uses xtfrm; xtfrm in turn tells us it will use == and > methods for the class of x[i].

First I'll define an object that I want to sort:

xx <- lapply(c(3,5,7,2,4), function(i) list(name=LETTERS[i], value=i))
class(xx) <- "myobj"

Now, since xtfrm works on the x[i]'s, I need to define a [ function that returns the desired elements but still with the right class

`[.myobj` <- function(x, i) {
class(x) <- "list"
structure(x[i], class="myobj")
}

Now we need == and > functions for the myobj class; this potentially could be smarter by vectorizing these properly; but for the sort function, we know that we're only going to be passing in myobj's of length 1, so I'll just use the first element to define the relations.

`>.myobj` <- function(e1, e2) {
e1[[1]]$value > e2[[1]]$value
}

`==.myobj` <- function(e1, e2) {
e1[[1]]$value == e2[[1]]$value
}

Now sort just works.

sort(xx)

It might be considered more proper to write a full Ops function for your object; however, to just sort, this seems to be all you need. See p.89-90 in Venables/Ripley for more details about doing this using the S3 style. Also, if you can easily write an xtfrm function for your objects, that would be simpler and most likely faster.

How to order a list by a custom function, discarding duplicates?

Adding another alternative, for completeness, regarding the "custom sort"/"custom unique" part of the question. By defining methods for certain functions (as seen in ?xtfrm) we can apply custom sort and unique functions to any list (or other object).

First, a "class" attribute needs to be added:

class(thresholds) = "thresholds"

Then, define the necessary custom functions:

"==.thresholds" = function(x, y) return(x[[1]][["value"]] == y[[1]][["value"]])
">.thresholds" = function(x, y) return(x[[1]][["value"]] > y[[1]][["value"]])
"[.thresholds" = function(x, i) return(structure(.subset(x, i), class = class(x)))
is.na.thresholds = function(x) return(is.na(x[[1]][["value"]]))

Now, we can apply sort:

sort(thresholds)

Finally, add a custom unique function:

duplicated.thresholds = function(x, ...) return(duplicated(sapply(x, function(elt) elt[["value"]])))
unique.thresholds = function(x, ...) return(x[!duplicated((x))])

And:

sort(unique(thresholds))

(Similar answers and more information here and here)

Sorting elements of a list column present in R's data.table

You can simplifiy your code and use unlist before you sort the list elements:

my_dt[, D := toString(sort(unlist(C))), by = 1:nrow(my_dt)][]
# A B C D
# 1: 1 9.245525 mango,pear,apple apple, mango, pear
# 2: 2 10.195239 mango,pear,apple apple, mango, pear
# 3: 3 13.277489 mango,pear,apple apple, mango, pear
# 4: 4 8.248815 mango,pear,apple apple, mango, pear
# 5: 5 10.243520 mango,pear,apple apple, mango, pear
# 6: 1 12.724261 mango,pear,apple apple, mango, pear
# 7: 2 9.530758 mango,pear,apple apple, mango, pear
# 8: 3 7.893234 mango,pear,apple apple, mango, pear
# 9: 4 8.260433 mango,pear,apple apple, mango, pear
#10: 5 9.219746 mango,pear,apple apple, mango, pear
#11: 1 8.305300 mango,pear,apple apple, mango, pear
#12: 2 9.478721 mango,pear,apple apple, mango, pear
#13: 3 9.171161 mango,pear,apple apple, mango, pear
#14: 4 9.633898 mango,pear,apple apple, mango, pear
#15: 5 10.814112 mango,pear,apple apple, mango, pear

If column D should be a list column, do

my_dt[, D := list(list(sort(unlist(C)))), by = 1:nrow(my_dt)]
my_dt

See Arun's answer from the post: Using lists inside data.table columns

How to sort a list of objects based on an attribute of the objects?

# To sort the list in place...
ut.sort(key=lambda x: x.count, reverse=True)

# To return a new list, use the sorted() built-in function...
newlist = sorted(ut, key=lambda x: x.count, reverse=True)

More on sorting by keys.

Sort a list by multiple attributes?

A key can be a function that returns a tuple:

s = sorted(s, key = lambda x: (x[1], x[2]))

Or you can achieve the same using itemgetter (which is faster and avoids a Python function call):

import operator
s = sorted(s, key = operator.itemgetter(1, 2))

And notice that here you can use sort instead of using sorted and then reassigning:

s.sort(key = operator.itemgetter(1, 2))

Sorting a list containing objects of a class in python

As far as the sorting is concerned, you definitely can use the key function:

import operator
lst.sort(key=lambda x: x.blocks)
lst.sort(key=operator.attrgetter('blocks') ) #alternative without lambda.

However, if you want to be able to compare stats objects in a non-sorting context, you can override __eq__,__gt__,__lt__ (and to make your life easier, you can use the functools.total_ordering class decorator to define most of the comparisons for you):

import functools
@functools.total_ordering
class stats(object): #inherit from object. It's a good idea
def __init__(self, fname, blocks, backEdges):
self.fname = fname
self.blocks = blocks
self.backEdges = backEdges
def printStats(self):
print self.fname + str(self.blocks) + str(self.backEdges)
def __eq__(self,other):
return self.blocks == other.blocks
def __lt__(self,other):
return self.blocks < other.blocks

with stats defined this way, sorting should again be as simple as:

lst.sort()  #or if you want a new list:  new_lst = sorted(lst)


Related Topics



Leave a reply



Submit