Как использовать PyCall в Юля для преобразования выходного Python для Юли таблицы данных


Я хотел бы получить некоторые данные из quandl и проанализировать их в Julia. К сожалению, официального API для этого пока нет. Я знаю это решение, но оно все еще довольно ограничено в функциональности и не следует тому же синтаксису, что и исходный API Python.

Я думал, что это будет умная вещь, чтобы использовать PyCall, чтобы получить данные, используя официальный API-интерфейс Python изнутри Джулии. Это действительно дает результат, но я не уверен, как я могу преобразовать его в формат, который я мог бы использовать внутри Julia (в идеале a DataFrame).

Я попробовал следующее.
using PyCall, DataFrames
@pyimport quandl

data = quandl.get("WIKI/AAPL", returns = "pandas");

Джулия преобразует этот вывод в Dict{Any,Any}. При использовании returns = "numpy" вместо returns = "pandas" я получаю PyObject rec.array.

Как я могу заставить data быть Джулией DataFrame, поскольку quandl.jl вернет ее? Обратите внимание, что quandl.jl не подходит для меня, потому что он не поддерживает автоматическое извлечение нескольких ресурсов и не имеет нескольких других функций, поэтому важно, чтобы я мог использовать Python ПРИКЛАДНОЙ ПРОГРАММНЫЙ ИНТЕРФЕЙС.

Спасибо за любые предложения!

3 5

3 ответа:

Вы столкнулись с различием в версиях Python/Pandas. У меня есть две конфигурации, которые легко доступны для меня: панды 0.18.0 в Python 2 и панды 0.19.1 в Python 3. Ответ @niczky12 при условии хорошо работает в первой конфигурации, но я вижу ваше Dict{Any,Any} поведение во второй конфигурации. В принципе, что-то меняется между этими двумя конфигурациями, так что PyCall обнаруживает подобный отображению интерфейс для объектов Pandas, а затем предоставляет этот интерфейс в качестве словарь через автоматическое преобразование. Здесь есть два варианта:

  1. Работа с интерфейсом словаря:

    data = quandl.get("WIKI/AAPL", returns = "pandas")
    cols = keys(data)
    df = DataFrame(Any[collect(values(data[c])) for c in cols], map(Symbol, cols))
    
  2. Явно отключите автоматическое преобразование и используйте интерфейс PyCall для извлечения столбцов, как niczky12 показано в другом ответе. Обратите внимание, что data[:Open] выполнит автоматическое преобразование в сопоставленный словарь, а data["Open"] просто вернет PyObject.

    data = pycall(quandl.get, PyObject, "WIKI/AAPL", returns = "pandas")
    cols = data[:columns]
    df = DataFrame(Any[Array(data[c]) for c in cols], map(Symbol, cols))
    

В обоих случаях, однако, обратите внимание, что все-важные индекс даты не включается в результирующий фрейм данных. Вы почти наверняка хотите добавить это в виде столбца:

df[:Date] = collect(data[:index])

Вот один из вариантов:

Сначала извлеките имена столбцов из объекта data:

julia> colnames = map(Symbol, data[:columns]);
12-element Array{Symbol,1}:
 :Open                
 :High                
 :Low                 
 :Close               
 :Volume              
 Symbol("Ex-Dividend")
 Symbol("Split Ratio")
 Symbol("Adj. Open")  
 Symbol("Adj. High")  
 Symbol("Adj. Low")   
 Symbol("Adj. Close") 
 Symbol("Adj. Volume")

Затем вылейте все свои столбцы в фрейм данных:

julia> y = DataFrame(Any[Array(data[c]) for c in colnames], colnames)

6×12 DataFrames.DataFrame
│ Row │ Open  │ High  │ Low   │ Close │ Volume   │ Ex-Dividend │ Split Ratio │
├─────┼───────┼───────┼───────┼───────┼──────────┼─────────────┼─────────────┤
│ 1   │ 28.75 │ 28.87 │ 28.75 │ 28.75 │ 2.0939e6 │ 0.0         │ 1.0         │
│ 2   │ 27.38 │ 27.38 │ 27.25 │ 27.25 │ 785200.0 │ 0.0         │ 1.0         │
│ 3   │ 25.37 │ 25.37 │ 25.25 │ 25.25 │ 472000.0 │ 0.0         │ 1.0         │
│ 4   │ 25.87 │ 26.0  │ 25.87 │ 25.87 │ 385900.0 │ 0.0         │ 1.0         │
│ 5   │ 26.63 │ 26.75 │ 26.63 │ 26.63 │ 327900.0 │ 0.0         │ 1.0         │
│ 6   │ 28.25 │ 28.38 │ 28.25 │ 28.25 │ 217100.0 │ 0.0         │ 1.0         │

│ Row │ Adj. Open │ Adj. High │ Adj. Low │ Adj. Close │ Adj. Volume │
├─────┼───────────┼───────────┼──────────┼────────────┼─────────────┤
│ 1   │ 0.428364  │ 0.430152  │ 0.428364 │ 0.428364   │ 1.17258e8   │
│ 2   │ 0.407952  │ 0.407952  │ 0.406015 │ 0.406015   │ 4.39712e7   │
│ 3   │ 0.378004  │ 0.378004  │ 0.376216 │ 0.376216   │ 2.6432e7    │
│ 4   │ 0.385453  │ 0.38739   │ 0.385453 │ 0.385453   │ 2.16104e7   │
│ 5   │ 0.396777  │ 0.398565  │ 0.396777 │ 0.396777   │ 1.83624e7   │
│ 6   │ 0.420914  │ 0.422851  │ 0.420914 │ 0.420914   │ 1.21576e7   │

Спасибо @Matt B. За предложения по упрощению кода.

Проблема в том, что типы столбцов находятся Any внутри фрейма данных. Чтобы сделать его немного более эффективным, вот несколько функций, которые выполняют эту работу:

# first, guess the Julia equivalent of type of the object
function guess_type(x::PyCall.PyObject)
  string_dtype = x[:dtype][:name]
  julia_string = string(uppercase(string_dtype[1]), string_dtype[2:end])

  return eval(parse("$julia_string"))
end

# convert an individual column, falling back to Any array if the guess was wrong
function convert_column(x)
  y = try Array{guess_type(x)}(x) catch Array(x) end
  return y
end

# put everything together into a single function
function convert_pandas(df)
  colnames =  map(Symbol, data[:columns])
  y = DataFrame(Any[convert_column(df[c]) for c in colnames], colnames)

  return y
end

Выше, когда применяется к вашему data, дает те же имена столбцов, что и раньше, но с правильными типами столбцов Float64:

y = convert_pandas(data);
showcols(y)
9147×12 DataFrames.DataFrame
│ Col # │ Name        │ Eltype  │ Missing │
├───────┼─────────────┼─────────┼─────────┤
│ 1     │ Open        │ Float64 │ 0       │
│ 2     │ High        │ Float64 │ 0       │
│ 3     │ Low         │ Float64 │ 0       │
│ 4     │ Close       │ Float64 │ 0       │
│ 5     │ Volume      │ Float64 │ 0       │
│ 6     │ Ex-Dividend │ Float64 │ 0       │
│ 7     │ Split Ratio │ Float64 │ 0       │
│ 8     │ Adj. Open   │ Float64 │ 0       │
│ 9     │ Adj. High   │ Float64 │ 0       │
│ 10    │ Adj. Low    │ Float64 │ 0       │
│ 11    │ Adj. Close  │ Float64 │ 0       │
│ 12    │ Adj. Volume │ Float64 │ 0       │

Существует API. Просто используй Квандл.дл: https://github.com/milktrader/Quandl.jl

using Quandl
data = quandlget("WIKI/AAPL")

Это имеет дополнительное преимущество в получении данных в удобном формате Julia (TimeArray), который имеет соответствующие методы, определенные для работы с такими данными.