I have a list of SparseArray
s, and I want to combine them into one array that concatenates their rules. I don't want to connect them together like ArrayFlatten
does, but rather to combine the defined values.
I'm working to look at a function that maps $\mathbb{N}$ to a list of points in $\mathbb{N^2}$, and I want to look at plots of the function's outputs ensure that all values in a relevant region of $\mathbb{N^2}$ are present in the image of at least one application. (There's other stuff going on in the SparseArray
values too.) So I'm taking the outputs of the function as SparseArray
s, and combining them.
This is the best solution I've got so far. It pretty much takes the rules from each input array and flattens them, with a substitution rule to get rid of defaults. But it seems that this should be a 1-liner; am I missing something obvious?
combineSparseArrays[arrays_, default_] :=
SparseArray[
Flatten[
(ArrayRules/@arrays) /. ({_Blank,_Blank}->_) -> Nothing,
1],
Automatic,
default]
As a note, my first version used normal arrays [as lists of lists] and combined them using a Do
loop that set elements in the output array individually. The SparseArray
version is, unsurprisingly, rather faster.
Edit to add example:
Here's an example input and expected output.
The easy-to-read version: as a list of lists of rules in grid form, where each row represents one input SparseArray
:
{1,3}->28 {2,3}->35 {3,3}->54 {_,_}->0
{1,4}->65 {2,4}->72 {3,4}->91 {_,_}->0
{1,5}->126 {2,5}->133 {3,5}->152 {4,5}->189 {_,_}->0
{1,6}->217 {2,6}->224 {3,6}->243 {4,6}->280 {5,6}->341 {_,_}->0
Same ruleset, in InputForm
for easy copy/paste:
{SparseArray[Automatic, {3, 3}, 0, {1, {{0, 1, 2, 3}, {{3}, {3}, {3}}}, {28, 35, 54}}],
SparseArray[Automatic, {3, 4}, 0, {1, {{0, 1, 2, 3}, {{4}, {4}, {4}}}, {65, 72, 91}}],
SparseArray[Automatic, {4, 5}, 0, {1, {{0, 1, 2, 3, 4}, {{5}, {5}, {5}, {5}}}, {126, 133, 152, 189}}],
SparseArray[Automatic, {5, 6}, 0, {1, {{0, 1, 2, 3, 4, 5}, {{6}, {6}, {6}, {6}, {6}}}, {217, 224, 243, 280, 341}}]}
The expected output should be equal to the following (which I reformatted for ease of reading):
SparseArray[{{1,3}->28, {1,4}->65, {1,5}->126,
{1,6}->217, {2,3}->35, {2,4}->72,
{2,5}->133, {2,6}->224, {3,3}->54,
{3,4}->91, {3,5}->152, {3,6}->243,
{4,5}->189, {4,6}->280, {5,6}->341,
{_,_}->0}]
In answer to klgr's question: Yes, it is possible for there to be duplicates, although there aren't in that example. These may be resolved by using either input array or something, but not by summation (although that's a clever idea!).
That did give me the idea of combining them into a 3d SparseArray
using Transpose
, like this:
Transpose[SparseArray[SparseArray[#, Max /@ Transpose[Dimensions /@ arrays]] & /@ arrays], {3,1,2}]
My plan had been to treat this as a 2d array of lists, and use SelectFirst
to get the first non-default element, but so far my attempts to do that have crashed the kernel.
arrays = {s1, s2}
dos1
ands2
have duplicate non-zero positions? If yes, how would you treat repeated entries when mergings
ands2
? If no, you can simply add the arrays:Plus @@ arrays
, if the dimensions are identical, and, if not,Plus @@ (SparseArray[#, Max /@ Transpose[Dimensions /@ arrays]] & /@ arrays)
$\endgroup$