Julia循环作用域问题:如何修改DataFrame值并保留到下一轮迭代?
问题:DataFrame迭代存储后所有元素均为最后一次结果
运行以下代码时,发现out0中存储的所有params DataFrame内容完全一致,全是最后一次迭代生成的值。但如果去掉player_id列,改用直接覆盖整个params的方式(注释掉的那行代码),结果就符合预期。
using DataFrames # Simulation out0 = Vector[]; nsim = Int(1e1); ## Initialize player parameters to random integers between 1 and 100 params = rand(1:100, (5,4)); params = DataFrame(params, :auto); params.player_id = 1:nrow(params); params = params[!, [5; 1:4]]; for n in 1:nsim # Aggregate current simulation results df_vector = DataFrame[]; push!(df_vector, params); # Add current results to final push!(out0, df_vector); # Update parameters params[!, 2:5] = max.(min.(params[!, 2:5] .+ rand(-2:2, 5, 4), 100), 0); # params = max.(min.(params .+ rand(-2:2, 5, 4), 100), 0); end
原因分析
Julia里的DataFrame是引用类型,当你把params推入数组时,并没有复制一份新的DataFrame,只是存了指向原对象的引用。后续修改params的列数据时,所有存在数组里的引用都会指向这个被修改后的对象,所以最后看到的全是最后一次的结果。
而直接覆盖params(params = ...)时,是创建了一个全新的DataFrame对象,让params指向它。之前存在数组里的引用还是指向旧的对象,不会被后续修改影响,所以结果正常。
解决方案
每次存储前,先复制一份params的副本再推入数组,用copy()函数即可:
for n in 1:nsim # Aggregate current simulation results df_vector = DataFrame[]; push!(df_vector, copy(params)); # 新增copy()创建副本 # Add current results to final push!(out0, df_vector); # Update parameters params[!, 2:5] = max.(min.(params[!, 2:5] .+ rand(-2:2, 5, 4), 100), 0); end
如果你的DataFrame包含嵌套的复杂结构,可以用deepcopy()替代copy(),但对于这个场景,copy()已经足够。
内容的提问来源于stack exchange,提问作者Livid
相关产品推荐
相关产品推荐

