ROLLUP

Prepare Fake Data
CREATE TABLE vaccines (country text, type text, effectiveness smallint) ;

DO $$
BEGIN
FOR n IN 1..10000 LOOP
INSERT INTO vaccines VALUES ( 'Japan', 'Phizer', 90+round(10*random()) ) ;
end LOOP;
end;
$$;

DO $$
BEGIN
FOR n IN 1..10000 LOOP
INSERT INTO vaccines VALUES ( 'Japan', 'Moderna', 90+round(10*random()) ) ;
END LOOP;
END;
$$;

DO $$
BEGIN
FOR n IN 1..10000 LOOP
INSERT INTO vaccines VALUES ( 'Australia', 'Phizer', 90+round(10*random()) ) ;
END LOOP;
END;
$$;

DO $$
BEGIN
FOR n IN 1..10000 LOOP
INSERT INTO vaccines VALUES ( 'Australia', 'Moderna', 90+round(10*random()) ) ;
END LOOP;
END;
$$;

Then you can use GROUP BY with ROLLUP to let Postgres return average for you:

SELECT country, type, AVG(effectiveness)
FROM vaccines
WHERE country IN ('Japan', 'Australia')
GROUP BY ROLLUP (country, type)
ORDER BY country ASC
;

  country  |  type   |         avg
-----------+---------+---------------------
 Australia |         | 94.9977000000000000
 Australia | Moderna | 94.9611000000000000
 Australia | Phizer  | 95.0343000000000000
 Japan     | Moderna | 94.9578000000000000
 Japan     | Phizer  | 95.0509000000000000
 Japan     |         | 95.0043500000000000
           |         | 95.0010250000000000

Note this is fake return data

The plan looks like and is using a MixedAggregate:

                                QUERY PLAN
---------------------------------------------------------------------------
 Sort  (cost=1336.19..1336.20 rows=7 width=47)
   Sort Key: country
   ->  MixedAggregate  (cost=0.00..1336.09 rows=7 width=47)
         Hash Key: country, type
         Hash Key: country
         Group Key: ()
         ->  Seq Scan on vaccines  (cost=0.00..736.00 rows=40000 width=17)
               Filter: (country = ANY ('{Japan,Australia}'::text[]))
(8 rows)

Time: 0.437 m

You see PostgreSQL inserts an extra row for avg for Japan and another one for Australia. And a row in the end for both Japan and Australia.

Related functions are: GROUPING SETS and CUBE.