I have found a few ways to solve the following problem, but I’m sure there are many others.

X=["Product_id","Date","Unit","Revenue","Product_id","Date","Unit","Revenue","Product_id","Date","Unit","Revenue"]
Y=["1234","01-2014-20","1","4321","2345","01-2014-21","2","4321","3211","01-2014-22","3","12345"]
df=DataFrame(X=X,Y=Y)
grp=groupby(df,:X)
###sol. 1#####
names=unique(df.X)
cols=[c.Y for c in grp]
dict=[n=>v for (n,v) in zip(names,cols)]
dfd1=DataFrame(dict)
###sol. 2#####
D=[c.X[1]=>c.Y for c in grp]
dfd2=DataFrame(D)

I would like to see solutions that make use of named-tuples, or the unstack function, or any other function suitable for the purpose.

Here’s a few (some of these produce additional columns but it’s easy to get rid of them if necessary):

vcat((permutedims(df[i:i+3, :], :X) for i in 1:4:nrow(df)-1)...)
vcat((permutedims(combine(grp, g->g[i,:]), :X) for i in 1:3)...)
# Using named tuples
DataFrame((; (Symbol.(df[i:i+3, :X]) .=> df[i:i+3, :Y])...) for i in 1:4:9)
DataFrame((; (Symbol(g[i, :X]) => g[i, :Y] for g in grp)...) for i in 1:3)
DataFrame([df[j+4i,2] for i in 0:2, j in 1:4], df[1:4, 1])
df.Z = repeat(1:3, inner=4)
unstack(df, :X, :Y)

Just what I was looking for. Thank you very much @sudete.
Could you explain me the rationale of the named tuple syntax?
That is, where does it (; (keys. => values) …) come from

You asked for named tuples so I looked for a way to use them

The DataFrame() constructor accepts an iterator of named tuples (one per row). You can construct a named tuple with (a=1, b=2) or (; a=1, b=2) (names are literal) or with (; :a => 1, :b => 2) (names can be any expression that returns a symbol).

In your problem the names are not literal, they are computed, so I have to use that last version with pairs.