In this case, what I would do is start with the dictionary, whose keys would be (C1, C2, C3) tuple and values would be a tuple of vectors (C4, C5).
Also, instead of missing in C4 and C5 I would rater use just an empty vector.
Next I would populate these dictionaries row by row (if a new row key is hit - adding it, and if old row key is found - appending data to the vectors.
This should be simple and efficient to populate. Later you can decide - either such a data structure is OK for you or, if not, you can relatively easily change it to a DataFrame.