如何使用Julia中的分类数据?

时间:2016-09-16 10:31:10

标签: julia

如何在Julia中存储分类变量?例如,像["Apple", "Orange", "Banana", "Orange", "Apple", "Banana", "Apple"]这样的字符串数组是否有任何合适的数据结构将上述数组视为分类类型?例如,在处理DNA序列时,我们需要处理大量不同长度的序列,那么表示和处理这些数据的最有效方法是什么?

4 个答案:

答案 0 :(得分:7)

答案 1 :(得分:5)

这取决于你想要用它做什么。以下是一些可能有用的常用工具:

稀疏矩阵

我经常使用的一个工具是稀疏矩阵。如果您已经不熟悉它们,那么基本要点是它们是一种有效的存储方式(存储方式)并使用大量零处理(处理速度快)矩阵。当使用分类数据进行大多数统计分析时,即使它已经完成了#34;通过统计程序,稀疏矩阵将用于分类变量的上下文中。具体而言,这些操作是将分类变量的每个值表示为数据矩阵中的单独列。对于统计分析,您通常也会将分类变量的一个值作为" base"状态是为了避免完美的共线性。

在任何情况下,下面是我为自己写的一个函数,我用它。它会将分类向量转换为稀疏矩阵。该功能有一些选项,您可以通过调整已注释的部分来修改:

  • 在矩阵中包含所有值,或将一个值作为" base"状态。

  • 输出一个单独的列名列表,然后可以用它来创建更大的总数据矩阵。

如果您有多个分类变量,您只需多次使用此函数,然后拼接最终的Array,DataFrame等。

它有点像自己做的"解决方案 - 可能有一些软件包可以更容易地执行特定的尝试,但可能不会。这样做的好处是可以为您提供一个非常通用且通用的数据结构,因此可以很容易地将其插入到您可能具有的数学方程或算法中,从这里指定您的分析。

function OneHot(x::Vector; header::Bool = false, drop::Bool = true)
    UniqueVals = unique(x)  ## note: don't sort this - that will mess up order of vals to idx.  
    Val_to_Idx = [Val => Idx for (Idx, Val) in enumerate(unique(x))] ## create a dictionary that maps unique values in the input array to column positions in the new sparse matrix.
    ColIdx = convert(Array{Int64}, [Val_to_Idx[Val] for Val in x])
    MySparse = sparse(collect(1:length(x)),  ColIdx, ones(Int32, length(x)))
    if drop
        StartIdx = 2
    else
        StartIdx = 1
    end
    if header
        return (MySparse[:,StartIdx:end], UniqueVals[StartIdx:end])  ## I.e. gives you back a tuple, second element is the header which you can then feed to something to name the columns or do whatever else with
    else
        return MySparse[:,StartIdx:end]  ## use MySparse[:, 2:end] to drop a value
    end
end

补充评论:

  • 如果您既可以使用分类变量,也可以使用连续变量,则可以将它们放在一个稀疏矩阵中,例如: sparse([A B])
  • 如果您打算这样做,并且您的连续变量存储为Float32Float64或其他类型,那么您也可以从ones(Int32, length(x))更改函数以创建无论你的连续数据是什么类型的,因为当你将稀疏矩阵与连续数据结合起来时,它们只会被转换为。

PooledDataArray

根据"自己操作"进入频谱的另一端,DataArrays包中有PooledDataArray类型。对于存储具有许多重复值的分类变量的数据,内存方式更为有效。

运行长度编码

这里另一个有用的工具是运行长度编码。如果你有一个值在一行中多次出现的向量,那么运行长度编码可以是一种更有效的存储和使用它的方法。 Julia有一个RLEVectors包(见here),我相信它的开发者有DNA和基因组学的东西作为他们的原始用例。

答案 2 :(得分:3)

DataStructures包中的计数器功能可能就是您所需要的。

julia> using DataStructures
julia> a = ["Apple", "Orange", "Banana", "Orange", "Apple",  "Banana", "Apple"]
julia> a_counter = counter(a)
DataStructures.Accumulator{ASCIIString,Int64}(Dict("Apple"=>3,"Orange"=>2,"Banana"=>2))
julia> a_counter["Apple"]
3

答案 3 :(得分:3)

我建议您创建自己的类型,将数据保存为整数索引数组,并包含"类别"字段,其中包含索引和类别名称之间的映射'他们自己,例如。

type Categorical
  data::Array{Int64}
  categories::Dict{Int64, String}
end

(也许可选择ordinal::Bool字段?)

一旦你有了这个,你可以适当地创建/重载方法,以你想要的方式直观地呈现结果,并通过他们的索引进行比较和其他操作。

<小时/> 的实施例
1)创建一个分类数组

julia> A = Categorical([1 2;2 1;2 2;1 1], Dict(1=>"male", 2=>"female"))

2)为分类对象创建默认显示样式:

julia> import Base.display
julia> display(A::Categorical) = display([A.categories[i] for i in A.data])
julia> A
4×2 Array{String,2}:
 "male"    "female"
 "female"  "male"  
 "female"  "female"
 "male"    "male"  

3)创建比较:

julia> import Base.(.==)
julia> (.==)(A::Categorical, B::Categorical) = A.data .== B.data
julia> A .== B
4×2 BitArray{2}:
  true   true
  true   true
 false  false
 false  false