DROP INDEX completely drops the index.
The transaction has to be committed before it takes effect for new queries, but that's not normally an issue. You are probably seeing other testing artifacts like:
- Postgres flips to a different query plan after statistics have slightly changed. That would indicate that your cost settings might be inappropriate or some other poor configuration.
- Repeated execution of the query has populated the cache (which can make a big difference for big tables). For halfway comparable results run all candidates a couple of times.
- Your query is based on "the last ten minutes". There can be 1000 rows and 10 minutes later, there can be just 1. Could make a big difference.
Query
For starters, remove completely unnecessary parts:
SELECT COUNT(*) AS count
FROM (
   SELECT 1
   FROM   loc_zones       z
   JOIN   loc_zone_events ze ON ze.zone_id = z.id
   JOIN   raw_events      r  ON r.id = ze.raw_event_id
   WHERE  z.app_id = 1
   AND    round(EXTRACT('epoch' FROM NOW() - ze.timestamp) / 60) BETWEEN 0 AND 10
   GROUP  BY r.device_id
   ) AS t;
Or:
SELECT COUNT(DISTINCT r.device_id) AS count
FROM   loc_zones       z
JOIN   loc_zone_events ze ON ze.zone_id = z.id
JOIN   raw_events      r  ON r.id = ze.raw_event_id
WHERE  z.app_id = 1
AND    round(EXTRACT('epoch' FROM NOW() - ze.timestamp) / 60) BETWEEN 0 AND 10
(Not necessarily faster, count(DISTINCT col) is no performance hero.)
But there is more:
Your WHERE condition round(...) is not sargable. To retrieve events of "the last 10 minutes", use instead:
...
AND    ze.timestamp >= now() - interval '10 min'
AND    ze.timestamp <  now();  -- only if there can be timestamps in the future 
This is sargable and can use an index on ze.timestamp.
Note: Your expression was using round() instead of trunc(), which effectively
covers the range (-0.5, 10.5), which are 11 minutes (not 10), or 10.5 minutes if there are no future timestamps. Deal with that difference one way or the other ...
Index
Since only the last 10 minutes seem to be relevant, you could further improve that with a partial index. The special difficulty here is the moving time frame. This related answer has a complete solution for that:
Building on that, you then have a partial index like:
CREATE INDEX ze_timestamp_recent_idx ON tbl (timestamp DESC);
WHERE  created_at > f_min_ts();
And adapt the query like:
WHERE  ...
AND    ze.timestamp > f_min_ts()   -- to match partial index
AND    ze.timestamp >= now() - interval '10 min'
AND    ze.timestamp <  now();
Aside: don't use the basic type name timestamp as column name.