pycall 0.1.0.alpha → 0.1.0.alpha.20170224

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9dfb955ab7f7606b2e9b757dc3c5f7b8aaa3b90b
4
- data.tar.gz: f2c110fd87edf0ed41eaaf150c8cef2bb45c5709
3
+ metadata.gz: 6d6ddea53f723b45bf9207c52746afaa083fba57
4
+ data.tar.gz: 1739c9f4e3a5d72503427540708f435694dea5aa
5
5
  SHA512:
6
- metadata.gz: 6203262df5efc0a06c35da7e17e40170ed87b537b4c718c786b981524547f05a4c3ee9cd238927e8028125344afcabf319fbe16ada076cad8d7b004c60926c35
7
- data.tar.gz: 592f154d6598fbf7528bd0302f857430f51727536e776b2b035fbef7a55ac32602e931fe6235bdf58f3a44b62c7842b7cfbb7319fa9da59813ed9cc500a8db4b
6
+ metadata.gz: '08a91ee3157d872d18725d57ace058d991feb1855250307d9b9cd17feab5081a96ed03ab05941ecec073c22c3a63b2ed8c8c777c83735459e9e7b8e10e7220d7'
7
+ data.tar.gz: 2359c6d09f3bc687466e41c0e0de2287a89bd4b11587e3384d4a41dd0eb737cf75af387d149efbb8322baa69136d4483f3901728e6b2b36545a9dbc5e6fe8d35
@@ -1,5 +1,19 @@
1
1
  sudo: false
2
2
  language: ruby
3
+
3
4
  rvm:
5
+ - 2.1.10
6
+ - 2.2.5
4
7
  - 2.3.1
5
- before_install: gem install bundler -v 1.13.2
8
+ - ruby-head
9
+
10
+ addons:
11
+ apt:
12
+ packages:
13
+ - python3
14
+ - python3-dev
15
+ - python3-all
16
+
17
+ before_install:
18
+ - gem update --system
19
+ - gem update bundler
data/Gemfile CHANGED
@@ -2,3 +2,8 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in pycall.gemspec
4
4
  gemspec
5
+
6
+ group :development do
7
+ gem 'guard-rspec', require: false
8
+ gem 'terminal-notifier-guard', require: false
9
+ end
@@ -0,0 +1 @@
1
+ ./config/Guardfile
data/README.md CHANGED
@@ -1,4 +1,6 @@
1
- # Pycall
1
+ # PyCall
2
+
3
+ [![Build Status](https://travis-ci.org/mrkn/pycall.svg?branch=master)](https://travis-ci.org/mrkn/pycall)
2
4
 
3
5
  Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/pycall`. To experiment with that code, run `bin/console` for an interactive prompt.
4
6
 
@@ -32,7 +34,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
32
34
 
33
35
  ## Contributing
34
36
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/pycall.
37
+ Bug reports and pull requests are welcome on GitHub at https://github.com/mrkn/pycall.
36
38
 
37
39
 
38
40
  ## License
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+ #
4
+ # This file was generated by Bundler.
5
+ #
6
+ # The application 'guard' is installed as part of a gem, and
7
+ # this file is here to facilitate running it.
8
+ #
9
+
10
+ require "pathname"
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
12
+ Pathname.new(__FILE__).realpath)
13
+
14
+ require "rubygems"
15
+ require "bundler/setup"
16
+
17
+ load Gem.bin_path("guard", "guard")
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+ #
4
+ # This file was generated by Bundler.
5
+ #
6
+ # The application 'rspec' is installed as part of a gem, and
7
+ # this file is here to facilitate running it.
8
+ #
9
+
10
+ require "pathname"
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
12
+ Pathname.new(__FILE__).realpath)
13
+
14
+ require "rubygems"
15
+ require "bundler/setup"
16
+
17
+ load Gem.bin_path("rspec-core", "rspec")
@@ -0,0 +1,30 @@
1
+ # More info at https://github.com/guard/guard#readme
2
+
3
+ if RUBY_PLATFORM =~ /darwin/
4
+ notification :terminal_notifier, app_name: 'pycall.gem ::', activate: 'com.googlecode.iTerm2'
5
+ end
6
+
7
+ directories %w(config lib spec).select { |d|
8
+ Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")
9
+ }
10
+
11
+ watch('config/Guardfile') do
12
+ UI.info "Exiting guard because config changed"
13
+ exit 0
14
+ end
15
+
16
+ guard :rspec, cmd: 'bin/rspec' do
17
+ require "guard/rspec/dsl"
18
+ dsl = Guard::RSpec::Dsl.new(self)
19
+
20
+ # RSpec files
21
+ rspec = dsl.rspec
22
+ watch(rspec.spec_helper) { rspec.spec_dir }
23
+ watch(rspec.spec_support) { rspec.spec_dir }
24
+ watch(rspec.spec_files)
25
+
26
+ # Ruby files
27
+ ruby = dsl.ruby
28
+ dsl.watch_spec_files_for(ruby.lib_files)
29
+ watch('lib/pycall/libpython.rb') { 'spec' }
30
+ end
@@ -0,0 +1,130 @@
1
+ require 'pycall/import'
2
+ include PyCall::Import
3
+
4
+ pyimport 'numpy', as: :np
5
+ pyimport 'matplotlib.pyplot', as: :plt
6
+ pyimport 'matplotlib.colors', as: :mplc
7
+ pyfrom 'sklearn.cross_validation', import: :train_test_split
8
+ pyfrom 'sklearn.preprocessing', import: :StandardScaler
9
+ pyfrom 'sklearn.datasets', import: %i(make_moons make_circles make_classification)
10
+ pyfrom 'sklearn.neighbors', import: :KNeighborsClassifier
11
+ pyfrom 'sklearn.svm', import: :SVC
12
+ pyfrom 'sklearn.tree', import: :DecisionTreeClassifier
13
+ pyfrom 'sklearn.ensemble', import: %i(RandomForestClassifier AdaBoostClassifier)
14
+ pyfrom 'sklearn.naive_bayes', import: :GaussianNB
15
+ pyfrom 'sklearn.discriminant_analysis', import: %i(LinearDiscriminantAnalysis QuadraticDiscriminantAnalysis)
16
+
17
+ h = 0.02 # step size in the mesh
18
+
19
+ names = [
20
+ 'Nearest Neighbors',
21
+ 'Linear SVM',
22
+ 'RBF SVM',
23
+ 'Decision Tree',
24
+ 'Random Forest',
25
+ 'AdaBoost',
26
+ 'Naive Bayes',
27
+ 'Linear Discriminant Analysis',
28
+ 'Quadratic Discriminant Analysis'
29
+ ]
30
+
31
+ classifiers = [
32
+ KNeighborsClassifier.(3),
33
+ SVC.(kernel: 'linear', C: 0.025),
34
+ SVC.(gamma: 2, C: 1),
35
+ DecisionTreeClassifier.(max_depth: 5),
36
+ RandomForestClassifier.(max_depth: 5, n_estimators: 10, max_features: 1),
37
+ AdaBoostClassifier.(),
38
+ GaussianNB.(),
39
+ LinearDiscriminantAnalysis.(),
40
+ QuadraticDiscriminantAnalysis.()
41
+ ]
42
+
43
+ x, y = make_classification.(
44
+ n_features: 2,
45
+ n_redundant: 0,
46
+ n_informative: 2,
47
+ random_state: 1,
48
+ n_clusters_per_class: 1
49
+ )
50
+
51
+ np.random.seed.(42)
52
+ x += 2 * np.random.random_sample.(x.shape)
53
+ linearly_separable = PyCall.tuple(x, y)
54
+
55
+ datasets = [
56
+ make_moons.(noise: 0.3, random_state: 0),
57
+ make_circles.(noise: 0.2, factor: 0.5, random_state: 1),
58
+ linearly_separable
59
+ ]
60
+
61
+ fig = plt.figure.(figsize: PyCall.tuple(27, 9))
62
+ i = 1
63
+ all = PyCall.slice(nil)
64
+ datasets.each do |ds|
65
+ x, y = ds
66
+ x = StandardScaler.().fit_transform.(x)
67
+ x_train, x_test, y_train, y_test = train_test_split.(x, y, test_size: 0.4)
68
+
69
+ x_min, x_max = np.min.(x[all, 0]) - 0.5, np.max.(x[all, 0]) + 0.5
70
+ y_min, y_max = np.min.(x[all, 1]) - 0.5, np.max.(x[all, 1]) + 0.5
71
+
72
+ xx, yy = np.meshgrid.(
73
+ np.linspace.(x_min, x_max, ((x_max - x_min)/h).round),
74
+ np.linspace.(y_min, y_max, ((y_max - y_min)/h).round),
75
+ )
76
+ mesh_points = np.dstack.(PyCall.tuple(xx.ravel.(), yy.ravel.()))[0, all, all]
77
+
78
+ # just plot the dataset first
79
+ cm = plt.cm.RdBu
80
+ cm_bright = mplc.ListedColormap.(["#FF0000", "#0000FF"])
81
+ ax = plt.subplot.(datasets.length, classifiers.length + 1, i)
82
+ # plot the training points
83
+ ax.scatter.(x_train[all, 0], x_train[all, 1], c: y_train, cmap: cm_bright)
84
+ # and testing points
85
+ ax.scatter.(x_test[all, 0], x_test[all, 1], c: y_test, cmap: cm_bright, alpha: 0.6)
86
+
87
+ ax.set_xlim.(np.min.(xx), np.max.(xx))
88
+ ax.set_ylim.(np.min.(yy), np.max.(yy))
89
+ ax.set_xticks.(PyCall.tuple())
90
+ ax.set_yticks.(PyCall.tuple())
91
+ i += 1
92
+
93
+ # iterate over classifiers
94
+ names.zip(classifiers).each do |name, clf|
95
+ ax = plt.subplot.(datasets.length, classifiers.length + 1, i)
96
+ clf.fit.(x_train, y_train)
97
+ scor = clf.score.(x_test, y_test)
98
+
99
+ # Plot the decision boundary. For that, we will assign a color to each
100
+ # point in the mesh [x_min, x_max]x[y_min, y_max]
101
+ begin
102
+ # not implemented for some
103
+ z = clf.decision_function.(mesh_points)
104
+ rescue
105
+ z = clf.predict_proba.(mesh_points)[all, 1]
106
+ end
107
+
108
+ # Put the result into a color plot
109
+ z = z.reshape.(xx.shape)
110
+ ax.contourf.(xx, yy, z, cmap: cm, alpha: 0.8)
111
+
112
+ # Plot also the training points
113
+ ax.scatter.(x_train[all, 0], x_train[all, 1], c: y_train, cmap: cm_bright)
114
+ # and testing points
115
+ ax.scatter.(x_test[all, 0], x_test[all, 1], c: y_test, cmap: cm_bright, alpha: 0.6)
116
+
117
+ ax.set_xlim.(np.min.(xx), np.max.(xx))
118
+ ax.set_ylim.(np.min.(yy), np.max.(yy))
119
+ ax.set_xticks.(PyCall.tuple())
120
+ ax.set_yticks.(PyCall.tuple())
121
+ ax.set_title.(name)
122
+
123
+ ax.text.(np.max.(xx) - 0.3, np.min.(yy) + 0.3, "%.2f" % scor, size: 15, horizontalalignment: 'right')
124
+
125
+ i += 1
126
+ end
127
+ end
128
+
129
+ fig.subplots_adjust.(left: 0.02, right: 0.98)
130
+ plt.show.()
@@ -0,0 +1,27 @@
1
+ require 'pycall/import'
2
+ include PyCall::Import
3
+
4
+ pyimport 'numpy', as: 'np'
5
+ pyimport 'matplotlib.mlab', as: 'mlab'
6
+ pyimport 'matplotlib.pyplot', as: 'plt'
7
+
8
+ np.random.seed.(0)
9
+
10
+ mu = 100
11
+ sigma = 15
12
+ x = mu + sigma * np.random.randn.(437)
13
+
14
+ num_bins = 50
15
+
16
+ fig, ax = plt.subplots.()
17
+
18
+ n, bins, patches = ax.hist.(x, num_bins, normed: 1)
19
+
20
+ y = mlab.normpdf.(bins, mu, sigma)
21
+ ax.plot.(bins, y, '--')
22
+ ax.set_xlabel.('Smarts')
23
+ ax.set_ylabel.('Probability density')
24
+ ax.set_title.('Histogram of IQ: $\mu=100$, $\sigma=15$')
25
+
26
+ fig.tight_layout.()
27
+ plt.show.()
@@ -0,0 +1,41 @@
1
+ require 'pycall/import'
2
+
3
+ include PyCall::Import
4
+
5
+ pyimport 'numpy', as: 'np'
6
+ pyimport 'matplotlib.pyplot', as: 'plt'
7
+
8
+ pyfrom 'sklearn.datasets', import: 'fetch_olivetti_faces'
9
+ pyfrom 'sklearn.ensemble', import: 'ExtraTreesClassifier'
10
+
11
+ # Number of cores to use to perform parallel fitting of the forest model
12
+ n_jobs = 1
13
+
14
+ # Load the faces datasets
15
+ data = fetch_olivetti_faces.()
16
+ x = data.images.reshape.(PyCall.tuple(PyCall.len(data.images), -1))
17
+ y = data.target
18
+
19
+ mask = y < 5 # Limit to 5 classes
20
+ x = x[mask]
21
+ y = y[mask]
22
+
23
+ # Build a forest and compute the pixel importances
24
+ puts "Fitting ExtraTreesClassifier on faces data with #{n_jobs} cores..."
25
+ t0 = Time.now
26
+ forest = ExtraTreesClassifier.(
27
+ n_estimators: 1_000,
28
+ max_features: 128,
29
+ n_jobs: n_jobs,
30
+ random_state: 0
31
+ )
32
+
33
+ forest = forest.fit.(x, y)
34
+ puts "done in %0.3fs" % (Time.now - t0)
35
+ importances = forest.feature_importances_
36
+ importances = importances.reshape.(data.images[0].shape)
37
+
38
+ # Plot pixel importances
39
+ plt.matshow.(importances, cmap: plt.cm.hot)
40
+ plt.title.("Pixel importances with forests of trees")
41
+ plt.show.()
@@ -0,0 +1,48 @@
1
+ require 'pycall/import'
2
+ include PyCall::Import
3
+
4
+ require 'benchmark'
5
+ pyimport :pandas, as: :pd
6
+ pyimport :seaborn, as: :sns
7
+ pyimport 'matplotlib.pyplot', as: :plt
8
+
9
+ array = Array.new(100_000) { rand }
10
+
11
+ trials = 100
12
+ results = { method: [], runtime: [] }
13
+
14
+ # Array#sum
15
+ trials.times do
16
+ results[:method] << 'sum'
17
+ results[:runtime] << Benchmark.realtime { array.sum }
18
+ end
19
+
20
+ # Array#inject(:+)
21
+ trials.times do
22
+ results[:method] << 'inject'
23
+ results[:runtime] << Benchmark.realtime { array.inject(:+) }
24
+ end
25
+
26
+ # while
27
+ def while_sum(ary)
28
+ sum, i, n = 0, 0, ary.length
29
+ while i < n
30
+ sum += ary[i]
31
+ i += 1
32
+ end
33
+ sum
34
+ end
35
+
36
+ trials.times do
37
+ results[:method] << 'while'
38
+ results[:runtime] << Benchmark.realtime { while_sum(array) }
39
+ end
40
+
41
+ # visualization
42
+
43
+ df = pd.DataFrame.(PyCall::Dict.new(results))
44
+ sns.barplot.(x: 'method', y: 'runtime', data: df)
45
+ plt.title.("Array summation benchmark (#{trials} trials)")
46
+ plt.xlabel.('Summation method')
47
+ plt.ylabel.('Average runtime [sec]')
48
+ plt.show.()
@@ -1,5 +1,15 @@
1
1
  require "pycall/version"
2
-
3
- module Pycall
4
- # Your code goes here...
5
- end
2
+ require "pycall/libpython"
3
+ require "pycall/pyobject"
4
+ require "pycall/pyerror"
5
+ require "pycall/eval"
6
+ require "pycall/types"
7
+ require "pycall/conversion"
8
+ require "pycall/pyobject_wrapper"
9
+ require "pycall/tuple"
10
+ require "pycall/list"
11
+ require "pycall/dict"
12
+ require "pycall/set"
13
+ require "pycall/slice"
14
+ require "pycall/utils"
15
+ require "pycall/init"
@@ -0,0 +1,120 @@
1
+ module PyCall
2
+ module Conversions
3
+ def self.from_ruby(obj)
4
+ case obj
5
+ when PyObject
6
+ obj
7
+ when PyObjectWrapper
8
+ obj.__pyobj__
9
+ when TrueClass, FalseClass
10
+ LibPython.PyBool_FromLong(obj ? 1 : 0)
11
+ when Integer
12
+ LibPython.PyInt_FromSsize_t(obj)
13
+ when Float
14
+ LibPython.PyFloat_FromDouble(obj)
15
+ when String
16
+ case obj.encoding
17
+ when Encoding::US_ASCII, Encoding::BINARY
18
+ LibPython.PyString_FromStringAndSize(obj, obj.bytesize)
19
+ else
20
+ obj = obj.encode(Encoding::UTF_8)
21
+ LibPython.PyUnicode_DecodeUTF8(obj, obj.bytesize, nil)
22
+ end
23
+ when Symbol
24
+ from_ruby(obj.to_s)
25
+ when Array
26
+ PyCall::List.new(obj).__pyobj__
27
+ else
28
+ LibPython.Py_None
29
+ end
30
+ end
31
+
32
+ def self.convert_to_boolean(py_obj)
33
+ 0 != LibPython.PyInt_AsSsize_t(py_obj)
34
+ end
35
+
36
+ def self.convert_to_integer(py_obj)
37
+ LibPython.PyInt_AsSsize_t(py_obj)
38
+ end
39
+
40
+ def self.convert_to_float(py_obj)
41
+ LibPython.PyFloat_AsDouble(py_obj)
42
+ end
43
+
44
+ def self.convert_to_complex(py_obj)
45
+ real = LibPython.PyComplex_RealAsDouble(py_obj)
46
+ imag = LibPython.PyComplex_ImagAsDouble(py_obj)
47
+ Complex(real, imag)
48
+ end
49
+
50
+ def self.convert_to_string(py_obj)
51
+ FFI::MemoryPointer.new(:string) do |str_ptr|
52
+ FFI::MemoryPointer.new(:int) do |len_ptr|
53
+ res = LibPython.PyString_AsStringAndSize(py_obj, str_ptr, len_ptr)
54
+ return nil if res == -1 # FIXME: error
55
+
56
+ len = len_ptr.get(:int, 0)
57
+ return str_ptr.get_pointer(0).read_string(len)
58
+ end
59
+ end
60
+ end
61
+
62
+ def self.convert_to_array(py_obj, force_list: true, array_class: Array)
63
+ case
64
+ when force_list || py_obj.kind_of?(LibPython.PyList_Type)
65
+ len = LibPython.PySequence_Size(py_obj)
66
+ array_class.new(len) do |i|
67
+ LibPython.PySequence_GetItem(py_obj, i).to_ruby
68
+ end
69
+ end
70
+ end
71
+
72
+ def self.convert_to_tuple(py_obj)
73
+ PyCall::Tuple.new(py_obj)
74
+ end
75
+ end
76
+
77
+ class PyObject
78
+ def to_ruby
79
+ return nil if self.null? || self.py_none?
80
+
81
+ case self
82
+ when LibPython.PyBool_Type
83
+ return Conversions.convert_to_boolean(self)
84
+
85
+ when LibPython.PyInt_Type
86
+ return Conversions.convert_to_integer(self)
87
+
88
+ when LibPython.PyLong_Type
89
+ # TODO: should make Bignum
90
+
91
+ when LibPython.PyFloat_Type
92
+ return Conversions.convert_to_float(self)
93
+
94
+ when LibPython.PyComplex_Type
95
+ return Conversions.convert_to_complex(self)
96
+
97
+ when LibPython.PyString_Type
98
+ return Conversions.convert_to_string(self)
99
+
100
+ when LibPython.PyUnicode_Type
101
+ py_str_ptr = LibPython.PyUnicode_AsUTF8String(self)
102
+ return Conversions.convert_to_string(py_str_ptr).force_encoding(Encoding::UTF_8)
103
+
104
+ when LibPython.PyList_Type
105
+ return Conversions.convert_to_array(self)
106
+
107
+ when LibPython.PyTuple_Type
108
+ return Conversions.convert_to_tuple(self)
109
+
110
+ when LibPython.PyDict_Type
111
+ return PyCall::Dict.new(self)
112
+
113
+ when LibPython.PySet_Type
114
+ return PyCall::Set.new(self)
115
+ end
116
+
117
+ self
118
+ end
119
+ end
120
+ end