ruby arrays csv hash stocks

importar desde CSV a la matriz Ruby, con el primer campo como clave hash, luego buscar el valor de un campo dada la fila de encabezado



arrays stocks (6)

Tal vez alguien pueda ayudarme.

Comenzando con un archivo CSV como ese:

Ticker,"Price","Market Cap" ZUMZ,30.00,933.90 XTEX,16.02,811.57 AAC,9.83,80.02

Me las arreglo para leerlos en una matriz:

require ''csv'' tickers = CSV.read("stocks.csv", {:headers => true, :return_headers => true, :header_converters => :symbol, :converters => :all} )

Para verificar los datos, esto funciona:

puts tickers[1][:ticker] ZUMZ

Sin embargo, esto no:

puts tickers[:ticker => "XTEX"][:price]

¿Cómo voy a convertir esta matriz en un hash usando el campo ticker como clave única, de modo que pueda buscar fácilmente cualquier otro campo asociativamente como se define en la línea 1 de la entrada? Tratando con muchas más columnas y filas.

¡Muy apreciado!


Así (también funciona con otros CSV, no solo con el que especificó):

require ''csv'' tickers = {} CSV.foreach("stocks.csv", :headers => true, :header_converters => :symbol, :converters => :all) do |row| tickers[row.fields[0]] = Hash[row.headers[1..-1].zip(row.fields[1..-1])] end

Resultado:

{"ZUMZ"=>{:price=>30.0, :market_cap=>933.9}, "XTEX"=>{:price=>16.02, :market_cap=>811.57}, "AAC"=>{:price=>9.83, :market_cap=>80.02}}

Puede acceder a los elementos en esta estructura de datos de esta manera:

puts tickers["XTEX"][:price] #=> 16.02

Editar (según comentario): para seleccionar elementos, puede hacer algo como

tickers.select { |ticker, vals| vals[:price] > 10.0 }


No como 1-liner, es decir, pero esto fue más claro para mí.

csv_headers = CSV.parse(STDIN.gets) csv = CSV.new(STDIN) kick_list = [] csv.each_with_index do |row, i| row_hash = {} row.each_with_index do |field, j| row_hash[csv_headers[0][j]] = field end kick_list << row_hash end


Para agregar a la respuesta de Michael Kohl, si desea acceder a los elementos de la siguiente manera

puts tickers[:price]["XTEX"] #=> 16.02

Puedes probar el siguiente fragmento de código:

CSV.foreach("Workbook1.csv", :headers => true, :header_converters => :symbol, :converters => :all) do |row| hash_row = row.headers[1..-1].zip( (Array.new(row.fields.length-1, row.fields[0]).zip(row.fields[1..-1])) ).to_h hash_row.each{|key, value| tickers[key] ? tickers[key].merge!([value].to_h) : tickers[key] = [value].to_h} end


Para obtener lo mejor de ambos mundos (lectura muy rápida de un archivo enorme Y los beneficios de un objeto Ruby CSV nativo), mi código se había convertido en este método:

$stock="XTEX" csv_data = CSV.parse IO.read(%`|sed -n "1p; /^#{$stock},/p" stocks.csv`), {:headers => true, :return_headers => false, :header_converters => :symbol, :converters => :all} # Now the 1-row CSV object is ready for use, eg: $company = csv_data[:company][0] $volatility_month = csv_data[:volatility_month][0].to_f $sector = csv_data[:sector][0] $industry = csv_data[:industry][0] $rsi14d = csv_data[:relative_strength_index_14][0].to_f

que está más cerca de mi método original, pero solo lee en un registro más la línea 1 del archivo de entrada csv que contiene los encabezados. Las instrucciones en línea sed se encargan de eso, y todo es notoriamente instantáneo. Esto es mejor que el last porque ahora puedo acceder a todos los campos desde Ruby, y de forma asociativa, sin preocuparme más por los números de las columnas como en el caso de awk .


Si bien esta no es una solución de Ruby 100% nativa a la pregunta original, si otros tropezaran aquí y se preguntaran qué tipo de llamada de awk utilicé por ahora, aquí está:

$dividend_yield = IO.readlines("|awk -F, ''$1==/"#{$stock}/" {print $9}'' datafile.csv")[0].to_f

donde $ stock es la variable que había asignado previamente al símbolo de una empresa (el campo clave wannabe). Resiste convenientemente los problemas al devolver 0,0 si: ticker o archivo o campo # 9 no encontrado / vacío, o si el valor no se puede escribir en un flotador. Entonces cualquier ''%'' posterior en mi caso se truncará bien.

Tenga en cuenta que en este punto uno podría agregar fácilmente más filtros dentro de awk para que IO.readlines devuelva una matriz de 1 dim de líneas de salida del CSV resultante más pequeño, por ejemplo.

awk -F, ''$9 >= 2.01 && $2 > 99.99 {print $0}'' datafile.csv

salidas en bash cuyas líneas tienen un DivYld (col 9) sobre 2.01 y precio (col 2) sobre 99.99. (Lamentablemente, no estoy usando la fila de encabezado para determinar los números de campo, que es donde finalmente esperaba una matriz de Ruby asociativa de búsqueda.)


CSV.read(file_path, headers:true, header_converters: :symbol, converters: :all).collect do |row| Hash[row.collect { |c,r| [c,r] }] end