You have 12 months of data in some raster form... You want some statistical parameter... There are areas of nodata, the extents are all the same... and ... you have a gazillion of these to do.
Sounds like you have a 'cube'... the original 'space-time' cube'
. You can pull space from a time slice... You can slice time through space. At every location on a 'grid' you have Z as a sequence over time.
Here is the code. I will use ascii files, but they don't have to be, you just need to prep your files before you use them.
Source data originally from .... here .... thanks Xander Bakker.
<SPAN class="keyword token">import</SPAN> os
<SPAN class="keyword token">import</SPAN> numpy <SPAN class="keyword token">as</SPAN> np
<SPAN class="keyword token">from</SPAN> textwrap <SPAN class="keyword token">import</SPAN> dedent<SPAN class="punctuation token">,</SPAN> indent
<SPAN class="keyword token">import</SPAN> arcpy
arcpy<SPAN class="punctuation token">.</SPAN>overwriteOutput <SPAN class="operator token">=</SPAN> <SPAN class="token boolean">True</SPAN>
ft <SPAN class="operator token">=</SPAN> <SPAN class="punctuation token">{</SPAN><SPAN class="string token">'bool'</SPAN><SPAN class="punctuation token">:</SPAN> <SPAN class="keyword token">lambda</SPAN> x<SPAN class="punctuation token">:</SPAN> repr<SPAN class="punctuation token">(</SPAN>x<SPAN class="punctuation token">.</SPAN>astype<SPAN class="punctuation token">(</SPAN><SPAN class="string token">'int32'</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">,</SPAN>
<SPAN class="string token">'float'</SPAN><SPAN class="punctuation token">:</SPAN> <SPAN class="string token">'{: 0.3f}'</SPAN><SPAN class="punctuation token">.</SPAN>format<SPAN class="punctuation token">}</SPAN>
np<SPAN class="punctuation token">.</SPAN>set_printoptions<SPAN class="punctuation token">(</SPAN>edgeitems<SPAN class="operator token">=</SPAN><SPAN class="number token">3</SPAN><SPAN class="punctuation token">,</SPAN> linewidth<SPAN class="operator token">=</SPAN><SPAN class="number token">80</SPAN><SPAN class="punctuation token">,</SPAN> precision<SPAN class="operator token">=</SPAN><SPAN class="number token">2</SPAN><SPAN class="punctuation token">,</SPAN> suppress<SPAN class="operator token">=</SPAN><SPAN class="token boolean">True</SPAN><SPAN class="punctuation token">,</SPAN>
threshold<SPAN class="operator token">=</SPAN><SPAN class="number token">80</SPAN><SPAN class="punctuation token">,</SPAN> formatter<SPAN class="operator token">=</SPAN>ft<SPAN class="punctuation token">)</SPAN>
np<SPAN class="punctuation token">.</SPAN>ma<SPAN class="punctuation token">.</SPAN>masked_print_option<SPAN class="punctuation token">.</SPAN>set_display<SPAN class="punctuation token">(</SPAN><SPAN class="string token">'-'</SPAN><SPAN class="punctuation token">)</SPAN> <SPAN class="comment token"># change to a single -</SPAN>
<SPAN class="comment token"># ---- Processing temporal ascii files ----</SPAN>
<SPAN class="comment token"># Header information</SPAN>
<SPAN class="comment token"># ncols 720</SPAN>
<SPAN class="comment token"># nrows 360</SPAN>
<SPAN class="comment token"># xllcorner -180</SPAN>
<SPAN class="comment token"># yllcorner -90</SPAN>
<SPAN class="comment token"># cellsize 0.5</SPAN>
<SPAN class="comment token"># NODATA_Value -9999</SPAN>
<SPAN class="comment token"># ---- Begin the process ----</SPAN>
<SPAN class="comment token">#</SPAN>
cols <SPAN class="operator token">=</SPAN> <SPAN class="number token">720</SPAN>
rows <SPAN class="operator token">=</SPAN> <SPAN class="number token">360</SPAN>
ll_corner <SPAN class="operator token">=</SPAN> arcpy<SPAN class="punctuation token">.</SPAN>Point<SPAN class="punctuation token">(</SPAN><SPAN class="operator token">-</SPAN><SPAN class="number token">180</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="operator token">-</SPAN><SPAN class="number token">90.0</SPAN><SPAN class="punctuation token">)</SPAN> <SPAN class="comment token"># to position the bottom left corner</SPAN>
dx_dy <SPAN class="operator token">=</SPAN> <SPAN class="number token">0.5</SPAN>
nodata <SPAN class="operator token">=</SPAN> <SPAN class="string token">'-9999'</SPAN>
<SPAN class="comment token">#</SPAN>
<SPAN class="comment token"># ---- create the basic workspace parameters, create masked arrays ----</SPAN>
<SPAN class="comment token">#</SPAN>
out_file <SPAN class="operator token">=</SPAN> r<SPAN class="string token">'c:\Data\ascii_samples\avg_yr.tif'</SPAN>
folder <SPAN class="operator token">=</SPAN> r<SPAN class="string token">'C:\Data\ascii_samples'</SPAN>
arcpy<SPAN class="punctuation token">.</SPAN>env<SPAN class="punctuation token">.</SPAN>workspace <SPAN class="operator token">=</SPAN> folder
ascii_files <SPAN class="operator token">=</SPAN> arcpy<SPAN class="punctuation token">.</SPAN>ListFiles<SPAN class="punctuation token">(</SPAN><SPAN class="string token">"*.asc"</SPAN><SPAN class="punctuation token">)</SPAN>
a_s <SPAN class="operator token">=</SPAN> <SPAN class="punctuation token">[</SPAN>folder <SPAN class="operator token">+</SPAN> <SPAN class="string token">'\{}'</SPAN><SPAN class="punctuation token">.</SPAN>format<SPAN class="punctuation token">(</SPAN>i<SPAN class="punctuation token">)</SPAN> <SPAN class="keyword token">for</SPAN> i <SPAN class="keyword token">in</SPAN> ascii_files<SPAN class="punctuation token">]</SPAN>
arrays <SPAN class="operator token">=</SPAN> <SPAN class="punctuation token">[</SPAN><SPAN class="punctuation token">]</SPAN>
<SPAN class="keyword token">for</SPAN> arr <SPAN class="keyword token">in</SPAN> a_s<SPAN class="punctuation token">:</SPAN>
a <SPAN class="operator token">=</SPAN> np<SPAN class="punctuation token">.</SPAN>mafromtxt<SPAN class="punctuation token">(</SPAN>arr<SPAN class="punctuation token">,</SPAN> dtype<SPAN class="operator token">=</SPAN><SPAN class="string token">'int'</SPAN><SPAN class="punctuation token">,</SPAN> comments<SPAN class="operator token">=</SPAN>'<SPAN class="comment token">#',</SPAN>
delimiter<SPAN class="operator token">=</SPAN><SPAN class="string token">' '</SPAN><SPAN class="punctuation token">,</SPAN> skip_header<SPAN class="operator token">=</SPAN><SPAN class="number token">6</SPAN><SPAN class="punctuation token">,</SPAN>
missing_values<SPAN class="operator token">=</SPAN>nodata<SPAN class="punctuation token">,</SPAN> usemask<SPAN class="operator token">=</SPAN><SPAN class="token boolean">True</SPAN><SPAN class="punctuation token">)</SPAN>
arrays<SPAN class="punctuation token">.</SPAN>append<SPAN class="punctuation token">(</SPAN>a<SPAN class="punctuation token">)</SPAN>
<SPAN class="comment token">#</SPAN>
<SPAN class="comment token"># ---- A sample calculation from the inputs.... calculate the mean ----</SPAN>
<SPAN class="comment token">#</SPAN>
N <SPAN class="operator token">=</SPAN> len<SPAN class="punctuation token">(</SPAN>arrays<SPAN class="punctuation token">)</SPAN> <SPAN class="comment token"># number of months... in this case</SPAN>
arr_shp <SPAN class="operator token">=</SPAN> <SPAN class="punctuation token">(</SPAN>N<SPAN class="punctuation token">,</SPAN><SPAN class="punctuation token">)</SPAN> <SPAN class="operator token">+</SPAN> arrays<SPAN class="punctuation token">[</SPAN><SPAN class="number token">0</SPAN><SPAN class="punctuation token">]</SPAN><SPAN class="punctuation token">.</SPAN>shape <SPAN class="comment token"># we want a (month, col, row) array</SPAN>
msk <SPAN class="operator token">=</SPAN> arrays<SPAN class="punctuation token">[</SPAN><SPAN class="number token">0</SPAN><SPAN class="punctuation token">]</SPAN><SPAN class="punctuation token">.</SPAN>mask <SPAN class="comment token"># clone the mask... assume they are the same</SPAN>
e <SPAN class="operator token">=</SPAN> np<SPAN class="punctuation token">.</SPAN>zeros<SPAN class="punctuation token">(</SPAN>arr_shp<SPAN class="punctuation token">,</SPAN> <SPAN class="string token">'int'</SPAN><SPAN class="punctuation token">)</SPAN> <SPAN class="comment token"># one way is create a zeros array and fill</SPAN>
<SPAN class="keyword token">for</SPAN> i <SPAN class="keyword token">in</SPAN> range<SPAN class="punctuation token">(</SPAN>len<SPAN class="punctuation token">(</SPAN>arrays<SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">:</SPAN>
e<SPAN class="punctuation token">[</SPAN>i<SPAN class="punctuation token">]</SPAN> <SPAN class="operator token">=</SPAN> arrays<SPAN class="punctuation token">[</SPAN>i<SPAN class="punctuation token">]</SPAN>
a <SPAN class="operator token">=</SPAN> np<SPAN class="punctuation token">.</SPAN>ma<SPAN class="punctuation token">.</SPAN>array<SPAN class="punctuation token">(</SPAN>e<SPAN class="punctuation token">,</SPAN> mask<SPAN class="operator token">=</SPAN>e<SPAN class="operator token">*</SPAN>msk<SPAN class="punctuation token">[</SPAN>np<SPAN class="punctuation token">.</SPAN>newaxis<SPAN class="punctuation token">,</SPAN> <SPAN class="punctuation token">:</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="punctuation token">:</SPAN><SPAN class="punctuation token">]</SPAN><SPAN class="punctuation token">)</SPAN> <SPAN class="comment token"># the empty array is ready</SPAN>
<SPAN class="comment token">#</SPAN>
<SPAN class="comment token"># ---- your need here... ie. Calculate a mean ----</SPAN>
<SPAN class="comment token">#</SPAN>
avg <SPAN class="operator token">=</SPAN> a<SPAN class="punctuation token">.</SPAN>mean<SPAN class="punctuation token">(</SPAN>axis<SPAN class="operator token">=</SPAN><SPAN class="number token">0</SPAN><SPAN class="punctuation token">)</SPAN> <SPAN class="comment token"># calculate the average down through the months</SPAN>
<SPAN class="comment token">#</SPAN>
<SPAN class="comment token"># ---- send it out to be viewed in ArcMap or ArcGIS Pro ----</SPAN>
<SPAN class="comment token">#</SPAN>
value_to_nodata <SPAN class="operator token">=</SPAN> int<SPAN class="punctuation token">(</SPAN>avg<SPAN class="punctuation token">.</SPAN>get_fill_value<SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">)</SPAN>
out <SPAN class="operator token">=</SPAN> avg<SPAN class="punctuation token">.</SPAN>view<SPAN class="punctuation token">(</SPAN>np<SPAN class="punctuation token">.</SPAN>ndarray<SPAN class="punctuation token">,</SPAN> fill_value<SPAN class="operator token">=</SPAN>value_to_nodata<SPAN class="punctuation token">)</SPAN>
g <SPAN class="operator token">=</SPAN> arcpy<SPAN class="punctuation token">.</SPAN>NumPyArrayToRaster<SPAN class="punctuation token">(</SPAN>out<SPAN class="punctuation token">,</SPAN> ll_corner<SPAN class="punctuation token">,</SPAN> dx_dy<SPAN class="punctuation token">,</SPAN> dx_dy<SPAN class="punctuation token">)</SPAN>
g<SPAN class="punctuation token">.</SPAN>save<SPAN class="punctuation token">(</SPAN>out_file<SPAN class="punctuation token">)</SPAN>
<SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN></SPAN>So the basic process is simple. I have coded this verbosely and used input parameters read manually from the ascii header since it is the quickest way.... and you would probably know what they are from the start.
So in this example... 12 months of some variable, averaged accounting for the nodata cells. Do the map stuff, define its projection... project it, give it some symbology and move on.

I will leave that for those that make maps.
Modify to suit... maybe I will get this into a toolbox someday
NOTE.....
Now in the linked example, there was a need to simply convert those to rasters from the input format. In that case you would simply consolidate the salient portions of the script as follows and create the output rasters within the masked array creation loop
<SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN>
<SPAN class="keyword token">for</SPAN> arr <SPAN class="keyword token">in</SPAN> a_s<SPAN class="punctuation token">:</SPAN>
a <SPAN class="operator token">=</SPAN> np<SPAN class="punctuation token">.</SPAN>mafromtxt<SPAN class="punctuation token">(</SPAN>arr<SPAN class="punctuation token">,</SPAN> dtype<SPAN class="operator token">=</SPAN><SPAN class="string token">'int'</SPAN><SPAN class="punctuation token">,</SPAN> comments<SPAN class="operator token">=</SPAN>'<SPAN class="comment token">#',</SPAN>
delimiter<SPAN class="operator token">=</SPAN><SPAN class="string token">' '</SPAN><SPAN class="punctuation token">,</SPAN> skip_header<SPAN class="operator token">=</SPAN><SPAN class="number token">6</SPAN><SPAN class="punctuation token">,</SPAN>
missing_values<SPAN class="operator token">=</SPAN>nodata<SPAN class="punctuation token">,</SPAN> usemask<SPAN class="operator token">=</SPAN><SPAN class="token boolean">True</SPAN><SPAN class="punctuation token">)</SPAN>
value_to_nodata <SPAN class="operator token">=</SPAN> int<SPAN class="punctuation token">(</SPAN>a<SPAN class="punctuation token">.</SPAN>get_fill_value<SPAN class="punctuation token">(</SPAN><SPAN class="punctuation token">)</SPAN><SPAN class="punctuation token">)</SPAN>
out <SPAN class="operator token">=</SPAN> a<SPAN class="punctuation token">.</SPAN>view<SPAN class="punctuation token">(</SPAN>np<SPAN class="punctuation token">.</SPAN>ndarray<SPAN class="punctuation token">,</SPAN> fill_value<SPAN class="operator token">=</SPAN>value_to_nodata<SPAN class="punctuation token">)</SPAN>
r <SPAN class="operator token">=</SPAN> arcpy<SPAN class="punctuation token">.</SPAN>NumPyArrayToRaster<SPAN class="punctuation token">(</SPAN>out<SPAN class="punctuation token">,</SPAN> ll_corner<SPAN class="punctuation token">,</SPAN> dx_dy<SPAN class="punctuation token">,</SPAN> dx_dy<SPAN class="punctuation token">)</SPAN>
out_file <SPAN class="operator token">=</SPAN> arr<SPAN class="punctuation token">.</SPAN>replace<SPAN class="punctuation token">(</SPAN><SPAN class="string token">".asc"</SPAN><SPAN class="punctuation token">,</SPAN> <SPAN class="string token">".tif"</SPAN><SPAN class="punctuation token">)</SPAN>
r<SPAN class="punctuation token">.</SPAN>save<SPAN class="punctuation token">(</SPAN>out_file<SPAN class="punctuation token">)</SPAN>
<SPAN class="keyword token">del</SPAN> r<SPAN class="punctuation token">,</SPAN> a
<SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="punctuation token">.</SPAN><SPAN class="line-numbers-rows"><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN><SPAN></SPAN></SPAN>
So for either doing statistical calculations for temporal data, or for format conversion... there are options available where arcpy and numpy play nice.