2017-09-29 4 views
0

Une fonctionnalité intéressante de DataFrames est qu'il peut stocker les colonnes avec différents types et il peut « reconnaître automatiquement » eux, par exemple:Comment convertir une matrice de type mixte à dataframe Julia reconnaissant les types de colonnes

using DataFrames, DataStructures 

df1 = wsv""" 
parName region forType    value 
vol  AL  broadL_highF  3.3055628012 
vol  AL  con_highF   2.1360975151 
vol  AQ  broadL_highF  5.81984502 
vol  AQ  con_highF   8.1462998309 
""" 
typeof(df1[:parName]) 
DataArrays.DataArray{String,1} 
typeof(df1[:value]) 
DataArrays.DataArray{Float64,1} 

Quand j'essaie cependant d'atteindre le même résultat à partir d'une matrice (importée de feuille de calcul) I « lâche » que la conversion automatique:

dataMatrix = [ 
    "parName" "region" "forType"  "value"; 
    "vol"  "AL"  "broadL_highF" 3.3055628012; 
    "vol"  "AL"  "con_highF"  2.1360975151; 
    "vol"  "AQ"  "broadL_highF" 5.81984502; 
    "vol"  "AQ"  "con_highF"  8.1462998309; 
] 
h = [Symbol(c) for c in dataMatrix[1,:]] 
vals = dataMatrix[2:end, :] 
df2 = convert(DataFrame,OrderedDict(zip(h,[vals[:,i] for i in 1:size(vals,2)]))) 

typeof(df2[:parName]) 
DataArrays.DataArray{Any,1} 
typeof(df2[:value]) 
DataArrays.DataArray{Any,1} 

Il y a plusieurs questions sur le SO sur la façon de convertir une matrice en une trame de données (par exemple, DataFrame from Array with Header, Convert Julia array to dataframe), mais aucune des réponses ne traite de la conversion d'une matrice de type mixte.

Comment est-ce que je pourrais créer un DataFrame à partir d'une matrice reconnaissant automatiquement le type des colonnes? (1) convertir le df (en utilisant le dictionnaire ou le constructeur de la matrice .. le premier est plus rapide) et ensuite appliquer try-catch pour la conversion de type (ma réponse d'origine); (2) convertir en chaîne, puis utiliser df.inlinetable (réponse Dan Getz); (3) vérifier le type de chaque élément et leur cohérence par colonne (réponse Alexander Morley).

Voici les résultats:

# second time for compilation.. further times ~ results 
@time toDf1(m) # 0.000946 seconds (336 allocations: 19.811 KiB) 
@time toDf2(m) # 0.000194 seconds (306 allocations: 17.406 KiB) 
@time toDf3(m) # 0.001820 seconds (445 allocations: 35.297 KiB) 

Ainsi, il est fou, la solution la plus efficace semble « verser l'eau » et de réduire le problème à une déjà résolu un ;-)

Merci pour toutes les réponses.

+0

Pourquoi ne pas vous venez de sauver un fichier CSV à partir de la feuille de calcul et le charger avec CSV.read()? Cela devrai prendre soin de ça. –

+0

@ MichaelK.Borregaard parce que j'ai un modèle qui charge tous les paramètres et les données de plusieurs feuilles, et je veux éviter de les exporter tous à cvs chaque fois que je fais un changement. – Antonello

Répondre

2

Une autre méthode consisterait réutiliser la solution de travail à savoir convertir la matrice en une chaîne appropriée pour DataFrames à consommer. Dans le code, c'est:

using DataFrames 

dataMatrix = [ 
    "parName" "region" "forType"  "value"; 
    "vol"  "AL"  "broadL_highF" 3.3055628012; 
    "vol"  "AL"  "con_highF"  2.1360975151; 
    "vol"  "AQ"  "broadL_highF" 5.81984502; 
    "vol"  "AQ"  "con_highF"  8.1462998309; 
] 

s = join(
    [join([dataMatrix[i,j] for j in indices(dataMatrix, 2)] 
    , '\t') for i in indices(dataMatrix, 1)], '\n') 

df = DataFrames.inlinetable(s; separator='\t', header=true) 

Le df résultant a ses types de colonnes par devinés dataframe. Non corrélé, mais cette réponse me rappelle le how a mathematician boils water joke

+0

La solution que je jouais avec impliqué en utilisant 'writecsv 'écrire à un' IOBuffer', puis 'readtable' à partir de ça, le mettre à ta gueule. Cela rappelle cela, mais je pense que c'est plus propre. –

+1

@ MichaelK.Borregaard L'écriture sur un IOBuffer était aussi la première version de cette solution (mais ensuite éditée à cette version quand je me suis débarrassé d'une tabulation). –

+0

Ah - sans rapport - mais comment avez-vous eu readtable pour accepter l'entrée IOBuffer? Je peux faire 'a = IOBuffer(); writecsv (a, dataMatrix); readcsv (prendre! (a), DataFrame)) ', mais je ne peux pas le faire fonctionner avec' CSV.read' ou 'readtable'. –

-1

Bien que je n'ai pas trouvé une solution complète, une partie est d'essayer de convertir les colonnes individuelles ex post:

""" 
    convertDf!(df) 

Try to convert each column of the converted df from Any to In64, Float64 or String (in that order).  
""" 
function convertDf!(df) 
    for c in names(df) 
     try 
      df[c] = convert(DataArrays.DataArray{Int64,1},df[c]) 
     catch 
      try 
       df[c] = convert(DataArrays.DataArray{Float64,1},df[c]) 
      catch 
       try 
        df[c] = convert(DataArrays.DataArray{String,1},df[c]) 
       catch 
       end 
      end 
     end 
    end 
end 

Bien sûrement incomplète, il suffit à mes besoins.

+0

Désolé mais c'est terrible: joie: –

+0

@ MichaelK.Borregaard bien .. si vous avez une meilleure solution ... ;-) – Antonello

+0

@ MichaelK.Borregaard ..et aussi, si vous me dites ce qui est ou pourquoi c'est terrible .. Je vais apprendre ;-) – Antonello

1

Bien que je pense qu'il pourrait y avoir une meilleure façon de faire tout cela, cela devrait faire ce que vous voulez.

df = DataFrame() 
for (ind,s) in enumerate(Symbol.(dataMatrix[1,:])) # convert first row to symbols and iterate through them. 
    # check all types the same else assign to Any 
    T = typeof(dataMatrix[2,ind]) 
    T = all(typeof.(dataMatrix[2:end,ind]).==T) ? T : Any 
    # convert to type of second element then add to data frame 
    df[s] = T.(dataMatrix[2:end,ind]) 
end 
1
mat2df(mat) = 
    DataFrame([[mat[2:end,i]...] for i in 1:size(mat,2)], Symbol.(mat[1,:])) 

semble fonctionner et est plus rapide que @ dan-Getz de réponse (au moins pour cette matrice de données) :)

using DataFrames, BenchmarkTools 

dataMatrix = [ 
    "parName" "region" "forType"  "value"; 
    "vol"  "AL"  "broadL_highF" 3.3055628012; 
    "vol"  "AL"  "con_highF"  2.1360975151; 
    "vol"  "AQ"  "broadL_highF" 5.81984502; 
    "vol"  "AQ"  "con_highF"  8.1462998309; 
] 

mat2df(mat) = 
    DataFrame([[mat[2:end,i]...] for i in 1:size(mat,2)], Symbol.(mat[1,:])) 

function mat2dfDan(mat) 
    s = join([join([dataMatrix[i,j] for j in indices(dataMatrix, 2)], '\t') 
       for i in indices(dataMatrix, 1)],'\n') 

    DataFrames.inlinetable(s; separator='\t', header=true) 
end 

-

julia> @benchmark mat2df(dataMatrix) 

BenchmarkTools.Trial: 
    memory estimate: 5.05 KiB 
    allocs estimate: 75 
    -------------- 
    minimum time:  18.601 μs (0.00% GC) 
    median time:  21.318 μs (0.00% GC) 
    mean time:  31.773 μs (2.50% GC) 
    maximum time:  4.287 ms (95.32% GC) 
    -------------- 
    samples:   10000 
    evals/sample:  1 

julia> @benchmark mat2dfDan(dataMatrix) 

BenchmarkTools.Trial: 
    memory estimate: 17.55 KiB 
    allocs estimate: 318 
    -------------- 
    minimum time:  69.183 μs (0.00% GC) 
    median time:  81.326 μs (0.00% GC) 
    mean time:  90.284 μs (2.97% GC) 
    maximum time:  5.565 ms (93.72% GC) 
    -------------- 
    samples:   10000 
    evals/sample:  1