MYSQL Optimize group by query with aggregators
INDEX(date,             -- to satisfy the WHERE; must be first
      id_num, expired)  -- to complete "covering"; must be last (either order)


Because it is "covering", it will run entirely in the index's BTree.
By putting date first, it looks through the minimal number of rows.

This is a rather unusual query, so don't expect the principles to carry over to other queries.

Even when you are somewhat happy with that, I may tell you how to make it 10 times as fast.  But first, I need to see SHOW CREATE TABLE and provide some clues of the date range and distribution of dates in the table.  Also, are the rows being inserted somewhat chronologically?  And are there any UPDATEs or DELETEs?

Summary Table?

Perhaps the table could be summarized into a Summary Table that has one row per month per id_num.  Then the original query would do most of the work 30x faster from that table, plus still do some work from the huge table.  The summary table would be incrementally augmented.