1
$\begingroup$

I have a set of data consisting of daily measurements of two parameters. The data is available as a list of strings. Different parameters are measured each day. In the sample below, on January 1, a parameter "ab" has the value 100 and "c" has the value 101. (My data has undesirably split the names of the parameters wherever the name contains more than one word.)

lis = {"January","1","a","b","100","c","101","January","2","c","de","f","102","g","h",103"}

and would like to convert this to:

res ={{DateObject[{2020, 1, 1}],"ab","100","c","101"},{DateObject[{2020,1,2}],"cdef","102","gh","103"}}

In this data, there is always a day number following a month name. The parameter names are always preceded and followed by numbers and need to be combined into a single string element. Thank you for any suggestions.

$\endgroup$
  • 2
    $\begingroup$ Are you really really sure that you can no longer fix the original and prevent the splitting? This is a pretty horrible format, and a lot of work. Also, how come "de" did not get split? $\endgroup$ – MarcoB Jan 10 at 22:59
  • $\begingroup$ Also, what happened to e.g. January 10? Did it become {"January", "1", "0"}, or {"January", "10"}? $\endgroup$ – MarcoB Jan 10 at 23:08
5
$\begingroup$

Gosh, what a mess! Pretty much the only thing that could be easily recognized is the month names, so let's start from there to tokenize the list of strings.

I have added another example to your list, just to make sure that it works for more than two examples, and defined a list of months:

list = {"January", "1", "a", "b", "100", "c", "101", 
        "January", "2", "c", "de", "f", "102", "g", "h", "103", 
        "February", "20", "a", "b", "20"};

months = {"January", "February", "March", "April", 
          "May", "June", "July", "August", 
          "September", "October", "November", "December"};

Start by finding the month names and using them as delimiters to split the list:

Flatten@Position[list, _String?(MemberQ[months, #] &)]~
  Join~{Length[list] + 1};
Range[#1, #2 - 1] & @@@ Partition[%, 2, 1];
chopped = list[[#]] & /@ %

(* Out: 
{{"January", "1", "a", "b", "100", "c", "101"}, 
 {"January", "2", "c", "de", "f", "102", "g", "h", "103"}, 
 {"February", "20", "a", "b", "20"}} *)

The use an Interpreter to turn the first two strings, guaranteed to be month and day, to a DateObject:

dated = Cases[
    chopped,
    {month_, day_, rest__} :> {Interpreter["Date"][StringJoin[month, day]], rest}
 ]

(* Out: 
 {{DateObject[{2020, 1, 1}, "Day", "Gregorian", -6.`], "a", "b", "100", "c", "101"},
  {DateObject[{2020, 1, 2}, "Day", "Gregorian", -6.`], "c", "de", "f", "102", "g", "h", "103"}, 
  {DateObject[{2020, 2, 20}, "Day", "Gregorian", -6.`], "a", "b", "20"}} 
*)

Finally define a conversion function that uses ToExpression to convert each token, then decide whether the converted form is a string (i.e. a measurement name), or a number (i.e. a measurement value); in the latter case, it returns the number, otherwise it returns the string.

ClearAll[converter]
converter[s_String] :=
  StringCases[s,
    PatternSequence[
      name : Repeated[LetterCharacter],
      value : Repeated[DigitCharacter]
    ] :> Sequence[name, ToExpression[value]]
  ]

... and apply it to the previous result:

Cases[
  dated,
  {date_DateObject, strings__} :> {date, Sequence @@ converter@StringJoin[strings]}
]

(* Out: 
 {{DateObject[{2020, 1, 1}, "Day", "Gregorian", -6.`], "ab", 100, "c", 101}, 
  {DateObject[{2020, 1, 2}, "Day", "Gregorian", -6.`], "cdef", 102, "gh", 103}, 
  {DateObject[{2020, 2, 20}, "Day", "Gregorian", -6.`], "ab", 20}} 
*)
|improve this answer|||||
$\endgroup$
  • $\begingroup$ Yes it is a mess. But thanks for a start at it. $\endgroup$ – Suite401 Jan 11 at 4:21
  • $\begingroup$ @Suite401 You are welcome. See my update answer: it should solve the whole problem. $\endgroup$ – MarcoB Jan 12 at 2:11
  • $\begingroup$ Thanks! a lot to study here. $\endgroup$ – Suite401 Jan 12 at 6:24

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.