1
$\begingroup$

I have data with missing values. I need all permutations which follow the two rules: Every year must be represented in each draw; and each draw must contain a minimum of two elements for each year. Here is the data.

assoc = <|
   "A" -> <|"1990" -> "Aretn1", "1991" -> "Aretn2", 
     "1992" -> "Aretn3", "1993" -> "Aretn4", "1994" -> "Aretn5"|>, 
   "B" -> <|"1990" -> "Bretn1", "1991" -> "Bretn2", 
     "1992" -> "Bretn3", "1993" -> "Bretn4", "1994" -> "Bretn5"|>, 
   "C" -> <|"1990" -> "", "1991" -> "", "1992" -> "", 
     "1993" -> "Cretn4", "1994" -> "Cretn5"|>, 
   "D" -> <|"1990" -> "Dretn1", "1991" -> "Dretn2", 
     "1992" -> "Dretn3", "1993" -> "", "1994" -> ""|>, 
   "E" -> <|"1990" -> "", "1991" -> "", "1992" -> "", 
     "1993" -> "Eretn4", "1994" -> "Eretn5"|>|>;

Using the brute strength method, here are the permutations that qualify

enter image description here

Clearly a picking function using an elaborate Module with Reap and Sow or Select would do it. But I am trying to use more code efficient methods of Query or Counts that Datasets and Associations were created for

$\endgroup$
1
  • 1
    $\begingroup$ You might not be able to do much better than Subsets followed by Select with an appropriate filter criterion $\endgroup$
    – Lukas Lang
    Commented Sep 15, 2022 at 17:01

1 Answer 1

1
$\begingroup$

This might not scale well.

Step 1: Normalize the data.

It's usually best to let absense represent itself rather than use special tokens.

normAssoc = DeleteCases[""] /@ assoc

Step 2: Reverse the mapping

Let's look at the data in terms of years, since we eventually want to use year coverage as a criterion.

byYear = Merge[KeyValueMap[Thread@*Reverse@*Rule, Keys /@ normAssoc], Identity]

This gives

<|"1990" -> {"A", "B", "D"}, "1991" -> {"A", "B", "D"}, "1992" -> {"A", "B", "D"}, "1993" -> {"A", "B", "C", "E"}, "1994" -> {"A", "B", "C", "E"}|>

Step 3: Create yearly options

We want each year represented twice. So, we can create all 2-element subsets for each year's "coverage group".

byYear2Choices = Subsets[#, {2}] & /@ byYear

This gives

<|"1990" -> {{"A", "B"}, {"A", "D"}, {"B", "D"}}, 
  "1991" -> {{"A", "B"}, {"A", "D"}, {"B", "D"}}, 
  "1992" -> {{"A", "B"}, {"A", "D"}, {"B", "D"}}, 
  "1993" -> {{"A", "B"}, {"A", "C"}, {"A", "E"}, {"B", "C"}, {"B", "E"}, {"C", "E"}}, 
  "1994" -> {{"A", "B"}, {"A", "C"}, {"A", "E"}, {"B", "C"}, {"B", "E"}, {"C", "E"}}|>

Depending on what you want, we might want to instead create subsets of at least size 2.

Step 4: Make a choice for each year

Now just select one of the choices that covers each year. There will be a lot of overlap, so we'll use Union to eliminate duplicates.

result = Union[Union@*Flatten /@ Tuples[Values[byYear2Choices]]]

This gives

{{"A", "B"}, 
 {"A", "B", "C"}, 
 {"A", "B", "D"}, 
 {"A", "B", "E"}, 
 {"A", "C", "D"}, 
 {"A", "D", "E"}, 
 {"B", "C", "D"}, 
 {"B", "D", "E"}, 
 {"A", "B", "C", "D"},
 {"A", "B", "C", "E"}, 
 {"A", "B", "D", "E"}, 
 {"A", "C", "D", "E"}, 
 {"B", "C", "D", "E"}, 
 {"A", "B", "C", "D", "E"}}
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.