# How to partition set of items into subsets with similar mean, variance and number of elements - looking for some help

Short version:
Given I have a list of 120 traits (happy, sad, etc.) evaluated on a number of scales ranging from -5 (for example highly negative) to +5 (for example highly positive), how can I group these traits into 6 sets of 20 items each, so that each set has a similar mean and variance of those scales?

A word of explanation: The 120 traits will be judged on whether or not they describe one of 6 social objects (20 traits per object) (using a 1-5 scale). Traits I will be using come from a bigger set (of 300 unique items) where each has been evaluated on measures like: valence, agency, communion and a couple of others.

I'd like to make sure that each social object was judged on traits that overall had a similar mean and (possibly) standard deviation, so that I don't end up, with one object that had very extreme traits (highly negative and highly positive) while others where somewhere in the middle.

R data example:

This is a minimal data example.
Note that in my real data, measures (valence etc) are not necessarily normally distributed and most likely correlated with other measures.

I did some reading and found out my problem might be partitioning related. Here is a R code I made up that does (more or less) what I'd like, but only based on one variable (let's pretented x are ratings of valence).

library(tidyverse)

set.seed(1)
x<- rnorm(120) # normal distribution is just an example
x<- sort(x)
g1<- rep(1:6, 20) # grouping strategy 1
g2<- rep(c(1:6, 6:1), 10) # grouping strategy 2
df<- tibble(x, g1, g2)

df %>%
group_by(g1) %>%
summarise(mean = mean(x), var = var(x))

df %>%
group_by(g2) %>%
summarise(mean = mean(x), var = var(x))


With the above code I can see that strategy #2 yields better results, as means of groups are more similar than with strategy #1. q

# A tibble: 6 x 3
g1       mean       var
<int>      <dbl>     <dbl>
1     1 0.01216328 0.8462127
2     2 0.05113158 0.8146632
3     3 0.08567107 0.7907553
4     4 0.13632257 0.7679591
5     5 0.16613787 0.7915441
6     6 0.20683895 0.8273733

# A tibble: 6 x 3
g2      mean       var
<int>     <dbl>     <dbl>
1     1 0.1078368 0.9842131
2     2 0.1094842 0.8930841
3     3 0.1104103 0.8232504
4     4 0.1115834 0.7368137
5     5 0.1077852 0.7200830
6     6 0.1111654 0.7093138


What I can't figure out is how to work with a couple of ranking variables (above example had only x).

In other words, what would be the best strategy to find the best possible grouping solution for a dataframe like this:

data<- data.frame(
trait_number = seq(1:120),
valence_rating = rnorm(120, mean = 0, sd = 3),
agency_rating = rnorm(120, mean = 0, sd = 3),
communion_rating = rnorm(120, mean = 0, sd = 3),
grouping = NA)


(note: once again rnorm is just an example for sample data. My real data is not normally distributed)

If I can explain anything more, please let me know in comments. Thank you.

• This thing is really bugging me and I would love to offer a bounty if only I could. – blazej Nov 24 '17 at 20:33
• I don't understand what you want to do. Given the data in your R code, you want to divide up the observations into groups? Like, group 1 would be observations with valence around -1, agency around 1, and communion around 0. Group 2 would be observations with valence around 2, agency around 0, and communion around 0. Etc? Like that or something else? – Bill Nov 25 '17 at 18:08
• Thanks for stopping by Bill. No that's (probably) not what I want. I'd like to get 6 sets of 20 elements each, where valence, agency and communion have similar mean and variance between and within those 6 sets. In other words I'd like to get set as similar as possible to each other. – blazej Nov 25 '17 at 18:20
• Unfortunately, this is not something I know about. I googled and found that this is a topic which comes up in cross-validation. The paper I link below suggests an algorithm which is 1) choose an element randomly and assign it to set 1. 2) find the closest 5 elements to the one you chose in step 1 and assign these to sets 2 through 6. 3) keep doing this until you run out of elements or your sets are full. You need to operationalize closest in step 2 with some distance measure. – Bill Nov 25 '17 at 19:05
• researchgate.net/profile/Jose_Moreno-Torres/publication/… – Bill Nov 25 '17 at 19:05

I have a mutlivariate nonparametric regression method that uses clusters. Using the cluster ID's to sort the data, then assigning a group to that sorted data yields the following results with your data df:

require(NNS)

a=NNS.reg(cbind(data$valence_rating,data$agency_rating,data$communion_rating), rep(1,length(data$trait_number)))\$Fitted.xy

setkey(a,NNS.ID)

colnames(a)[1:3]=colnames(data[,2:4])

# Re-run from here with different group innovations
a[,:= (group=rep(1:6, 20))]

a[,{valence_mean=mean(valence_rating);valence_sd=sd(valence_rating)
agency_mean=mean(agency_rating);agency_sd=sd(agency_rating)
communion_mean=mean(communion_rating);communion_sd=sd(communion_rating)
list(valence_mean=valence_mean,valence_sd=valence_sd,
agency_mean=agency_mean,agency_sd=agency_sd,
communion_mean=communion_mean,communion_sd=communion_sd)
}, by=group]

group valence_mean valence_sd agency_mean agency_sd communion_mean communion_sd
1:     1  -0.06839077   2.674746 -0.61403779  3.024703     -0.1573398     2.455025
2:     2  -0.73164854   3.603836 -0.06347979  2.197635      0.5897524     3.638972
3:     3   0.87995369   2.425216 -0.70785458  2.915249     -0.1070705     3.266067
4:     4   0.34903443   2.667286 -0.67996477  3.295273      0.4793330     2.250582
5:     5   0.32653867   3.204289  0.35996665  3.191802      0.5791942     3.786095
6:     6   0.82249408   3.037100 -0.18225506  3.531984     -0.3244441     3.280554


I'm sure it could be better, but hopefully this gets you started and it's applicable to your actual data.