View and @view in DataFrames

I was surprised that view and @view work differently when we work with subsets of the data.
The following does not modify the parent dataframe

df = DataFrame(a = [0, 0, 0, 0], b = [1, 1, -1, missing], c = [1, -1, missing, 1])

dfv = dropmissing(df, [:b,:c], view = true)
dfv = view(dfv[dfv.b.>0, :], :, :)
dfv[1, 1] = 100 #it does not change df

display(df)
4×3 DataFrame
 Row │ a      b        c       
     │ Int64  Int64?   Int64?  
─────┼─────────────────────────
   1 │     0        1        1
   2 │     0        1       -1
   3 │     0       -1  missing 
   4 │     0  missing        1

But the following does change the parent DataFrame

df = DataFrame(a = [0, 0, 0, 0], b = [1, 1, -1, missing], c = [1, -1, missing, 1])

dfv = dropmissing(df, [:b,:c], view = true)
dfv = @view dfv[dfv.b.>0, :]
dfv[1, 1] = 100 #it changes df

display(df)
4×3 DataFrame
 Row │ a      b        c       
     │ Int64  Int64?   Int64?  
─────┼─────────────────────────
   1 │   100        1        1
   2 │     0        1       -1
   3 │     0       -1  missing 
   4 │     0  missing        1


I know that this could be done in other ways (e.g., with subset and skipmissing=true), but I was wondering if this is expected behavior.

This is expected behavior as in this expression dfv[dfv.b.>0, :] makes a copy first, of which you make a view.

While @view dfv[dfv.b.>0, :] does not make a copy, but just makes a view.

dfv = view(dfv[dfv.b.>0, :], :, :) should be dfv = view(view(dfv,dfv.b.>0, :), :, :) to make a view (actually then you can drop outer view call)

To see:

julia> @macroexpand(@view df[x1 .> 0.1, :])
:(true && (view)(df, x1 .> 0.1, :))
2 Likes