If your pav_type values are sequential, then you could use generate_series in place of an external table:
select p.pav_type, count(yes)
from generate_series(0,3) as p(pav_type)
left outer join shelter_inventory s
on p.pav_type = s.pav_type and s.yes = 1
group by p.pav_type
order by p.pav_type
This yields:
pav_type | count
----------+-------
0 | 1
1 | 2
2 | 1
3 | 0
This:
generate_series(0,3) as p(pav_type)
essentially generates an inlined table with a single column called pav_type and four rows: 0, 1, 2, 3. And you need to have the s.yes = 1 in the join condition (rather than the WHERE) because you want the yes = 0 values to be in the pre-grouped result set; if s.yes = 1 is in the WHERE clause then the yes = 0 rows won't be counted regardless of what the join condition is.
If your pav_types do not nicely fit with generate_series (i.e. not sequential or step-sequential) and you only have a small number of them, you could join to a VALUES expression:
select p.pav_type, count(yes)
from (values (0), (1), (2), (3)) as p(pav_type)
left outer join shelter_inventory s
on p.pav_type = s.pav_type and s.yes = 1
group by p.pav_type
order by p.pav_type
You need to make sure you get all the parentheses in the right places of course.
If you do have the pav_types in a separate table then do a LEFT OUTER JOIN to that table instead of using the generate_series. If your pav_types are not sequential and you have too many to sensibly put in a VALUES expression, then build a table to hold the valid pav_type values and LEFT OUTER JOIN to that.