From 4b8e18a19f375b8d4c12621ec8f8f9ee305d11fa Mon Sep 17 00:00:00 2001 From: gebele Date: Wed, 12 Oct 2016 13:45:37 +0000 Subject: stage save for transmission --- Gemfile | 1 + application.rb | 121 +++++++++++++++++++++++++++++++++++++++++++------- config.ru | 6 +++ views/predict.haml | 52 +++++++++++++++------- views/prediction.haml | 25 +++++++---- 5 files changed, 167 insertions(+), 38 deletions(-) diff --git a/Gemfile b/Gemfile index a33d28e..7692587 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,7 @@ source "https://rubygems.org" gemspec gem "lazar", :path => "../lazar" +gem "qsar-report", :path => "../qsar-report" gem "sinatra" gem 'sinatra-reloader', '~> 1.0' gem "haml" diff --git a/application.rb b/application.rb index 29c1aca..a702d9a 100644 --- a/application.rb +++ b/application.rb @@ -1,9 +1,11 @@ +require 'qsar-report' require 'rdiscount' $ambit_search = "http://data.enanomapper.net/substance?type=name&search=" $npo_search = "http://bioportal.bioontology.org/search?q=%s&ontologies=NPO&include_properties=false&include_views=false&includeObsolete=false&require_definition=false&exact_match=false&categories=" + configure :development do - $logger = Logger.new(STDOUT) + #$logger = Logger.new(STDOUT) end before do @@ -13,27 +15,111 @@ end get '/?' do redirect to('/predict') end -=begin -get '/qsar-report/:id' do +#=begin +get '/qmrf-report/:id' do + prediction_model = OpenTox::Model::NanoPrediction.find(params[:id]) + if prediction_model + model = prediction_model.model + model_type = "regression" + report = OpenTox::QMRFReport.new + if File.directory?("#{File.dirname(__FILE__)}/../../lazar") + lazar_commit = `cd #{File.dirname(__FILE__)}/../../lazar; git rev-parse HEAD`.strip + lazar_commit = "https://github.com/opentox/lazar/tree/#{lazar_commit}" + else + lazar_commit = "https://github.com/opentox/lazar/releases/tag/v#{Gem.loaded_specs["lazar"].version}" + end + report.value "QSAR_title", "Model for #{prediction_model.species} #{prediction_model.endpoint}" + report.change_catalog :software_catalog, :firstsoftware, {:name => "nano-lazar", :description => "nano-lazar toxicity predictions", :number => "1", :url => "https://nano-lazar.in-silico.ch", :contact => "helma@in-silico.ch"} + report.ref_catalog :QSAR_software, :software_catalog, :firstsoftware + report.value "qmrf_date", "#{Time.now.strftime('%d %B %Y')}" + report.change_catalog :authors_catalog, :firstauthor, {:name => "Christoph Helma", :affiliation => "in silico toxicology gmbh", :contact => "Rastatterstrasse 41, CH-4057 Basel, Switzerland", :email => "helma@in-silico.ch", :number => "1", :url => "http://in-silico.ch"} + report.ref_catalog :qmrf_authors, :authors_catalog, :firstauthor + report.change_catalog :authors_catalog, :modelauthor, {:name => "Christoph Helma", :affiliation => "in silico toxicology gmbh", :contact => "Contact Address", :email => "Contact Email", :number => "1", :url => "Web Page"} + report.ref_catalog :model_authors, :authors_catalog, :modelauthor + report.value "model_date", "#{Time.parse(model.created_at.to_s).strftime('%Y')}" + report.change_catalog :publications_catalog, :publications_catalog_1, {:title => "Rautenberg, Gebele and Helma (2013), Validation of read across predictions for nanoparticle toxicities ", :url => "in preparation"} + report.ref_catalog :references, :publications_catalog, :publications_catalog_1 + report.value "model_species", prediction_model.species + report.change_catalog :endpoints_catalog, :endpoints_catalog_1, {:name => prediction_model.endpoint, :group => ""} + report.ref_catalog :model_endpoint, :endpoints_catalog, :endpoints_catalog_1 + report.value "endpoint_units", "#{prediction_model.unit}" + report.value "algorithm_type", "#{model.class.to_s.gsub('OpenTox::Model::Lazar','')}" + #TODO add more + report.change_catalog :algorithms_catalog, :algorithms_catalog_1, {:definition => "see Helma 2016 and lazar.in-silico.ch, submitted version: #{lazar_commit}", :description => "Neighbor algorithm: #{model.neighbor_algorithm.gsub('_',' ').titleize}#{(model.neighbor_algorithm_parameters[:min_sim] ? ' with similarity > ' + model.neighbor_algorithm_parameters[:min_sim].to_s : '')}"} + report.ref_catalog :algorithm_explicit, :algorithms_catalog, :algorithms_catalog_1 + report.change_catalog :algorithms_catalog, :algorithms_catalog_3, {:definition => "see Helma 2016 and lazar.in-silico.ch, submitted version: #{lazar_commit}", :description => "modified k-nearest neighbor #{model_type}"} + report.ref_catalog :algorithm_explicit, :algorithms_catalog, :algorithms_catalog_3 + if model.prediction_algorithm_parameters + pred_algorithm_params = (model.prediction_algorithm_parameters[:method] == "rf" ? "random forest" : model.prediction_algorithm_parameters[:method]) + end + report.change_catalog :algorithms_catalog, :algorithms_catalog_2, {:definition => "see Helma 2016 and lazar.in-silico.ch, submitted version: #{lazar_commit}", :description => "Prediction algorithm: #{model.prediction_algorithm.gsub('OpenTox::Algorithm::','').gsub('_',' ').gsub('.', ' with ')} #{(pred_algorithm_params ? pred_algorithm_params : '')}"} + report.ref_catalog :algorithm_explicit, :algorithms_catalog, :algorithms_catalog_2 + + # Descriptors in the model 4.3 + if model.neighbor_algorithm_parameters[:type] + report.change_catalog :descriptors_catalog, :descriptors_catalog_1, {:description => "", :name => "#{model.neighbor_algorithm_parameters[:type]}", :publication_ref => "", :units => ""} + report.ref_catalog :algorithms_descriptors, :descriptors_catalog, :descriptors_catalog_1 + end + + # Descriptor selection 4.4 + report.value "descriptors_selection", "#{model.feature_selection_algorithm.gsub('_',' ')} #{model.feature_selection_algorithm_parameters.collect{|k,v| k.to_s + ': ' + v.to_s}.join(', ')}" if model.feature_selection_algorithm + response['Content-Type'] = "application/xml" + + return report.to_xml + else + bad_request_error "model with id: #{params[:id]} does not exist." + end end -=end +#=end get '/predict/?' do + # temporarily outstanding + nos = %w(S40.CIT + S40.MES + S40.MUTA + S40.cPEG5K-SH + S40.DDT@DOTAP + S40.MAA + S40.PLL-SH + S40.mPEG5K-SH + S40.MBA + S40.PVA + S40.LA + S40.AUT + S40.HDA + S40.SA + S40.nPEG5K-SH + S40.MHDA + ) @prediction_models = [] prediction_models = OpenTox::Model::NanoPrediction.all prediction_models.each{|m| m.model[:feature_selection_algorithm_parameters]["category"] == "P-CHEM" ? @prediction_models[0] = m : @prediction_models[1] = m} + # define type (pc or pcp) @prediction_models.each_with_index{|m,idx| idx == 0 ? m[:pc_model] = true : m[:pcp_model] = true} - # collect nanoparticles by training dataset (Ag + Au) dataset = OpenTox::Dataset.find_by(:name=> "Protein Corona Fingerprinting Predicts the Cellular Interaction of Gold and Silver Nanoparticles") - nanoparticles = dataset.nanoparticles - # select physchem_parameters by relevant_features out of each model - @@pc_relevant_features = @prediction_models[0].model.relevant_features.collect{|id, v| OpenTox::Feature.find(id)} - @@pcp_relevant_features = @prediction_models[1].model.relevant_features.collect{|id, v| OpenTox::Feature.find(id)} + # temporarily delete silver + nanoparticles = dataset.nanoparticles#.delete_if{|n| !nos.include?(n.name)} + + # select physchem_parameters by relevant_features out of each model; use @@ for global usage (prediction.haml) + @pc_relevant_features = @prediction_models[0].model.relevant_features.collect{|id, v| OpenTox::Feature.find(id)} + @pcp_relevant_features = @prediction_models[1].model.relevant_features.collect{|id, v| OpenTox::Feature.find(id)} + # check for outstanding nanoparticles pcp = nanoparticles.sample - pcp.physchem_descriptors.delete_if{|id,v| !@@pcp_relevant_features.include?(OpenTox::Feature.find(id))} + #while nos.include?(pcp.name) + # pcp = nanoparticles.sample + #end + # use only relevant features + pcp.physchem_descriptors.delete_if{|id,v| !@pcp_relevant_features.include?(OpenTox::Feature.find(id))} @example_pcp = pcp + + # check for outstanding nanoparticles pc = nanoparticles.sample - pc.physchem_descriptors.delete_if{|id,v| !@@pc_relevant_features.include?(OpenTox::Feature.find(id))} + #while nos.include?(pc.name) + # pcp = nanoparticles.sample + #end + # use only relevant features + #pc.physchem_descriptors.delete_if{|id,v| OpenTox::Feature.find(id).category != "P-CHEM"} + pc.physchem_descriptors.delete_if{|id,v| !@pc_relevant_features.include?(OpenTox::Feature.find(id))} @example_pc = pc haml :predict @@ -45,7 +131,6 @@ get '/license' do end post '/predict/?' do - # choose the right prediction model prediction_model = OpenTox::Model::NanoPrediction.find(params[:prediction_model]) size = params[:size].to_i @@ -64,7 +149,13 @@ post '/predict/?' do input_coating = in_coating input_pc = {} - (1..size).each{|i| input_pc["#{params["input_key_#{i}"]}"] = [params["input_value_#{i}"].to_f]} + (1..size).each{|i| input_pc["#{params["input_key_#{i}"]}"] = [params["input_value_#{i}"].to_f] unless params["input_value_#{i}"] == "-"} + + + # define relevant_features by input + @type = "pc" ? (@pc_relevant_features = input_pc.collect{|id,v| OpenTox::Feature.find(id)}) : (@pc_relevant_features = []) + @type = "pcp" ? (@pcp_relevant_features = input_pc.collect{|id,v| OpenTox::Feature.find(id)}) : (@pcp_relevant_features = []) + if input_pc == example_pc && input_core == example_core && input_coating == example_coating # unchanged input = database hit nanoparticle = OpenTox::Nanoparticle.find_by(:id => params[:example_id]) @@ -81,9 +172,9 @@ post '/predict/?' do @match = false @nanoparticle = nanoparticle end - # output + # prediction output @input = input_pc @prediction = prediction_model.model.predict_substance nanoparticle - + haml :prediction end diff --git a/config.ru b/config.ru index eec777d..36438a3 100644 --- a/config.ru +++ b/config.ru @@ -4,4 +4,10 @@ require 'bundler' Bundler.require require File.expand_path './application.rb' require "sinatra/reloader" if development? + +FileUtils.mkdir_p 'log' unless File.exists?('log') +log = File.new("log/nano-lazar.log", "a") +$stdout.reopen(log) +$stderr.reopen(log) + run Sinatra::Application diff --git a/views/predict.haml b/views/predict.haml index 1cd2916..0630b2c 100644 --- a/views/predict.haml +++ b/views/predict.haml @@ -14,7 +14,7 @@ = "#{idx+1}. #{m.model.feature_selection_algorithm_parameters[:category].nil? ? "Physchem & Proteomics" : "Physchem" }" %div.tab-content - @prediction_models.each_with_index do |m, idx| - - m[:pc_model] ? (example = @example_pc; type = "pc") : (example = @example_pcp; type = "pcp") + - m[:pc_model] ? (example = @example_pc; type = "pc"; relevant_features = @pc_relevant_features) : (example = @example_pcp; type = "pcp"; relevant_features = @pcp_relevant_features) #model.tab-pane{:id=>"#{idx}", :class => ("active" if idx == 0)} %b Model: %br @@ -38,8 +38,20 @@ %br Unit: = m.unit + %br + Prediction algorithm: + = m.model.prediction_algorithm.split(".").last.gsub("_"," ") + %br + Prediction algorithm parameter: + = "random forest" if m.model.prediction_algorithm_parameters["method"] == "rf" + %br + Neighbor algorithm: + = m.model.neighbor_algorithm.gsub("_", " ") + %br + Neighbor algorithm parameter: + = "min sim = #{m.model.neighbor_algorithm_parameters["min_sim"]}" %p - - cv = OpenTox::CrossValidation.find(m.repeated_crossvalidation_id) + -#- cv = OpenTox::CrossValidation.find(m.repeated_crossvalidation_id) %b Independent crossvalidations (log2 transformed): - crossvalidations =[] - m.repeated_crossvalidation.crossvalidation_ids.each{|cv| c = OpenTox::Validation::CrossValidation.find(cv); crossvalidations << c} @@ -55,18 +67,18 @@ = "Num unpredicted" = cv.nr_unpredicted %br - = "Root mean squared error:\t" + %a.ht5{:href=>"https://en.wikipedia.org/wiki/Root-mean-square_deviation", :rel=>"external"} RMSE: = cv.rmse.round(3) if cv.rmse %br - = "Mean absolute error:\t" + %a.ht5{:href=>"https://en.wikipedia.org/wiki/Mean_absolute_error", :rel=>"external"} MAE: = cv.mae.round(3) if cv.mae %br - = "R square:\t" + %a.ht5{:href=>"https://en.wikipedia.org/wiki/Coefficient_of_determination", :rel=>"external"}= "R"+"2"+":" = cv.r_squared.round(3) if cv.r_squared %br - //%b QSAR report - //%br - //%a{:href=>to('/qsar-report/'+m.id)} download + %b QMRF report + %p + %a.btn.btn-default{:href=>to('/qmrf-report/'+m.id), :rel=>"external"} download %hr %form{:id=>idx, :role=>"form", :action=> to("/predict"), :method=>"post"} %h3.help-block @@ -79,13 +91,15 @@ %input{:id=>"example_core",:type=>"hidden",:name=>"example_core",:value=>"#{example.core}"} %input{:id=>"example_coating",:type=>"hidden",:name=>"example_coating",:value=>"#{example.coating}"} %input{:id=>"example_pc",:type=>"hidden",:name=>"example_pc",:value=>"#{example.physchem_descriptors}"} - - size = example.physchem_descriptors.size + -#- size = example.physchem_descriptors.size + - size = relevant_features.size %input{:id=>"size",:type=>"hidden",:name=>"size",:value=>size} %input{:id=>"id",:type=>"hidden",:name=>"example_id",:value=>example.id} // input form parameters to transfer %div.form-group %label{:for=>"selCore#{idx}"} Core - %select.form-control{:id=>"selCore#{idx}", :name=>"input_core",:value=>example.core["name"]} + /%p= example.core["name"] + %select.form-control{:id=>"selCore#{idx}", :autocomplete=>"off", :name=>"input_core",:value=>example.core["name"]} %option{:selected => ("selected" if example.core["name"] == "Ag")} Ag %option{:selected => ("selected" if example.core["name"] == "Au")} Au %input{:id=>"input_core",:type=>"hidden",:name=>"in_core",:value=>"#{example.core}"} @@ -95,14 +109,20 @@ %input{:id=>"input_coating",:type=>"hidden",:name=>"in_coating",:value=>example.coating} // prediction model id %input{:id=>"prediction_model",:type=>"hidden",:name=>"prediction_model",:value=>m.id} - - example.physchem_descriptors.sort_by{|d| OpenTox::Feature.find(d[0]).category}.each_with_index do |v,id| - - feature = OpenTox::Feature.find_by(:id => v[0]) + - relevant_features.sort_by{|d| d.category}.each_with_index do |relf,id| + -#- example.physchem_descriptors.sort_by{|d| OpenTox::Feature.find(d[0]).category}.each_with_index do |v,id| + - feature = relf + /%p= feature.id + - v = example.physchem_descriptors.find{|id,v| id == feature.id.to_s } + /%p= v + -#- feature = OpenTox::Feature.find_by(:id => v[0]) + -#- feature = v - name = feature.name - if feature[:conditions] && !feature[:conditions]["MEDIUM"].blank? - name = feature.name + " / " + feature[:conditions]["MEDIUM"] - else - name = feature.name - - val = v[1] + - val = !v.nil? ? v[1] : "-" - id = id + 1 - if feature.category == "Proteomics" %h5 @@ -113,8 +133,10 @@ %h5 %a{:href=>$npo_search % string, :rel=>"external"}= name // input physchem parameters - %input.input-sm.form-control{:id=>id,:type=>"text",:name=>"input_value_#{id}",:value=>val[0]} - %input{:id=>id,:type=>"hidden",:name=>"input_key_#{id}",:value=>v[0]} + %input.input-sm.form-control{:id=>id,:type=>"text",:name=>"input_value_#{id}",:value=>"#{val[0] if val[0]}", :disabled=>("disabled" if val[0] == "-")} + - if val[0] == "-" + %input{:id=>id,:type=>"hidden",:name=>"input_value_#{id}",:value=>"-"} + %input{:id=>id,:type=>"hidden",:name=>"input_key_#{id}",:value=>feature.id} %hr #predict %button.btn.btn-success{:id=>"submitbutton", :type=>"submit", :onclick=>"showcircle();"} diff --git a/views/prediction.haml b/views/prediction.haml index 8f72a23..e6b4ae4 100644 --- a/views/prediction.haml +++ b/views/prediction.haml @@ -15,6 +15,8 @@ headerTemplate: '{content} {icon}', widgets: ['uitheme', 'staticRow', 'stickyHeaders'], widgetOptions: { + stickyHeaders_attachTo: 'neighbors', + stickyHeaders_offset: 0, stickyHeaders_xScroll : null }, sortList: [[1,1]], @@ -41,7 +43,8 @@ Net cell association %br [mL/ug(Mg)] - - @input.sort_by{|d| OpenTox::Feature.find(d[0]).category}.each do |key| + -#- @input.sort_by{|d| OpenTox::Feature.find(d[0]).category}.each do |key| + - @input.each{|d| OpenTox::Feature.find(d[0]).category}.each do |key| - feature = OpenTox::Feature.find_by(:id=>key[0]) - name = feature.name - if (feature[:conditions] && !feature[:conditions]["MEDIUM"].blank?) @@ -88,12 +91,16 @@ %h5.th5 Prediction: = @prediction[:value].round(3) %br - - if @prediction[:rmse] + - if @prediction[:value].blank? + %h5.th5 Prediction: + = "not available" + %br + -#- if @prediction[:rmse] %h5.th5 %a{:href=>"https://en.wikipedia.org/wiki/Root-mean-square_deviation", :rel=>"external"} RMSE: = @prediction[:rmse].round(3) %br - - if @prediction[:r_squared] + -#- if @prediction[:r_squared] %h5.th5 %a{:href=>"https://en.wikipedia.org/wiki/Coefficient_of_determination", :rel=>"external"}= "R"+"2"+":" = @prediction[:r_squared].round(3) @@ -111,7 +118,8 @@ - if !@prediction[:value] && !@prediction[:measurements] %h5.th5 not available / physchem - - @input.sort_by{|d| OpenTox::Feature.find(d[0]).category}.each do |v| + -#- @input.sort_by{|d| OpenTox::Feature.find(d[0]).category}.each do |v| + - @input.each do |v| %td.physchem %div{:style=>"display:inline-block;padding-right:20px;"} - if v[1].nil? @@ -123,7 +131,7 @@ - if @prediction[:neighbors] - @prediction[:neighbors].each_with_index do |neighbor,idx| - nano = OpenTox::Nanoparticle.find(neighbor["_id"]) - - pc_descriptors = nano.physchem_descriptors.delete_if{|id,v| OpenTox::Feature.find(id).category != "P-CHEM"} if @type == "pc" + -#- nano.physchem_descriptors.delete_if{|id,v| OpenTox::Feature.find(id).category != "P-CHEM"} if @type == "pc" %tr / ID %td @@ -144,8 +152,9 @@ %h5.th5 Measurement: = neighbor["measurements"][0] / Physchem - - nano.physchem_descriptors.delete_if{|id,v| @type == "pc" ? !@@pc_relevant_features.include?(OpenTox::Feature.find(id)) : !@@pcp_relevant_features.include?(OpenTox::Feature.find(id))}.sort_by{|id,v| OpenTox::Feature.find(id).category}.each do |k,v| + -#- nano.physchem_descriptors.delete_if{|id,v| @type == "pc" ? !@pc_relevant_features.include?(OpenTox::Feature.find(id)) : !@pcp_relevant_features.include?(OpenTox::Feature.find(id))}.sort_by{|id,v| OpenTox::Feature.find(id).category}.each do |k,v| + - nano.physchem_descriptors.delete_if{|id,v| @type == "pc" ? !@pc_relevant_features.include?(OpenTox::Feature.find(id)) : !@pcp_relevant_features.include?(OpenTox::Feature.find(id))}.sort_by{|id,v| @pc_relevant_features.index OpenTox::Feature.find(id)}.each do |k,v| %td.physchem - %div + -#%div %div{:style=>"display:inline-block;padding-right:20px;"} - = v[0].round(3) unless v.nil? + = v[0].round(3) unless v.nil? -- cgit v1.2.3