4
$\begingroup$

The City of Edmonton, close to where I live, just set a record for the number of consecutive days the min. temp was below zero (167 and counting...)

I found a site to download weather data for Edmonton https://edmonton.weatherstats.ca/download.html

My question is, once I get a list of temps into Mathematica, how would I check for "long" runs of negative values. I.E. If I had 10 years of min. temp values in a list, how would I find the longest list of values that are all below zero?

$\endgroup$
8
$\begingroup$

You can use UnitStep to convert negative numbers to 0 and positive numbers to 1. Then, you are just looking for the longest run of 0s. There are several answers for this, but I couldn't find a canonical version to point to after a brief look. So, here is a function that does this for you:

longestNegativeSequenceLength[v_]:=With[{bool = UnitStep[v]},
    Max[
        Length /@ Split[bool][[bool[[1]]+1 ;; -1 ;; 2]]
    ]
]

For example:

SeedRandom[1]
data = RandomReal[{-1, 1}, 20]

longestNegativeSequenceLength[data]

{0.634779, -0.777161, 0.579052, -0.624394, -0.517278, -0.868522, 0.0844932, -0.537691, -0.207988, 0.400948, -0.576348, 0.497314, -0.154299, -0.50501, 0.954344, 0.650326, 0.85055, 0.156112, -0.414261, -0.583898}

3

Addendum

To address the actual question of finding the sequence of negative values, you could do:

longestNegativeSequence[v_] := Module[{split = SplitBy[v, Negative]},
    First @ MaximalBy[split[[If[v[[1]]<0, 1, 2] ;; -1 ;; 2]], Length]
]

longestNegativeSequence[data]

{-0.624394, -0.517278, -0.868522}

$\endgroup$
  • $\begingroup$ The answers were, as always, instructive and helpful. Yes... based on a sample of 10000 days of weather data, we are (currently) at 167 days with a min. temp that is below zero. $\endgroup$ – Tom De Vries Apr 16 '18 at 14:20
6
$\begingroup$

Carl Woll's answer is instructive, but I think for people newer to Mathematica using the sequence functions would seem clearer and more direct.

SeedRandom[1]
data = RandomReal[{-1, 1}, 20];

threshold = 0;
pos = SequencePosition[If[# < threshold, 1, 0] & /@ data, {1 ..}];
data[[Span @@ #]] & /@ Take[Reverse@SortBy[pos, #[[2]] - #[[1]] &], 3]

(* {{-0.624394, -0.517278, -0.868522}, {-0.414261, -0.583898}, {-0.154299, -0.50501}}*)
$\endgroup$
6
$\begingroup$

In version 11.3 we have SequenceSplit which is good for this:

SeedRandom[1]
data = RandomReal[{-1, 1}, 20];

MaximalBy[Length] @ SequenceSplit[data, {_?Positive ..}]

(* {{-0.624394, -0.517278, -0.868522}} *)
$\endgroup$
2
$\begingroup$

A nice little oneliner without SequenceSplit is

First@MaximalBy[Length]@Select[Negative@*First]@SplitBy[#, Sign] &

Sadly there's no operator form of SplitBy, otherwise it would be a lovely litle point-free definition.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.