Okay. The hard part is generating the names. This should do what you want
julia> using Statistics, DataFrames;
julia> test=DataFrame(x1=rand(1000), x2=rand(1000), x3=rand(1000), x4=rand(1000));
julia> test.subject="a"
test[501:1000,5]= "b";
julia> function getproduct(names)
collect.( (vec ∘ collect ∘ Iterators.product)(names, names))
end;
julia> function dostuff(x, y)
mean(x) - mean(y)
end;
julia> combine(groupby(test, "subject"), getproduct(names(test, Not("subject"))) .=> dostuff)
2×17 DataFrame. Omitted printing of 8 columns
│ Row │ subject │ x1_x1_dostuff │ x2_x1_dostuff │ x3_x1_dostuff │ x4_x1_dostuff │ x1_x2_dostuff │ x2_x2_dostuff │ x3_x2_dostuff │ x4_x2_dostuff │
│ │ String │ Float64 │ Float64 │ Float64 │ Float64 │ Float64 │ Float64 │ Float64 │ Float64 │
├─────┼─────────┼───────────────┼───────────────┼───────────────┼───────────────┼───────────────┼───────────────┼───────────────┼───────────────┤
│ 1 │ a │ 0.0 │ 0.000719563 │ 2.31758e-5 │ -0.00244945 │ -0.000719563 │ 0.0 │ -0.000696388 │ -0.00316901 │
│ 2 │ b │ 0.0 │ 0.0272609 │ 0.0261707 │ 0.0326415 │ -0.0272609 │ 0.0 │ -0.00109015 │ 0.00538059 │
(side note, I invite the reader to think about how hard this would be in dplyr)