ValueError: Integers to negative integer powers are not allowed.[Python][Numpy]
エラー内容
以下のように、負の数を指数とした累乗の計算でエラーが出た。
for num in np.arange(-5, 5): print(10**num) """ ValueError: Integers to negative integer powers are not allowed. """
「負の整数による整数のべき乗は許されない」とのこと。
解決策
Python標準のrange
メソッドを使う。
for num in range(-5, 5): print(10**num)
numpyでは負の整数による整数のべき乗はできない。
逆に言えば、整数でなければnumpyでも期待通りの動作をする:
for num in np.arange(-5, 5): print(10.**num)
numpyは整数型の癖の強さに定評がある。
SQL何もわからんな人へ捧げる『SQL Bolt』
SQLBolt - Learn SQL - Introduction to SQL
SQLをWeb上で学べる教材。記述したクエリの結果がリアルタイムに出力される。
以下、コードサンプルといくつかの演習問題に対する解答例。簡単すぎる問題は省略。
SQL Lesson 3: Queries with constraints (Pt. 2)
-- 1. Find all the Toy Story movies SELECT * FROM movies where title like '%Toy Story%' ; -- 3. Find all the movies (and director) not directed by John Lasseter SELECT * FROM movies where director not like '%John Lasseter%' ;
SQL Review: Simple SELECT Queries
SELECT * FROM north_american_cities where Country like 'United States' order by latitude ASC;
SQL Lesson 7: OUTER JOINs
-- Find the list of all buildings that have employees SELECT distinct e.building FROM employees e join Buildings b on b.building_name = e.building ; -- List all buildings and the distinct employee roles in each building (including empty buildings) SELECT distinct b.building_name, e.role FROM Buildings b left outer join employees e on b.building_name = e.building ;
SQL Lesson 8: A short note on NULLs
SELECT column, another_column, … FROM mytable WHERE column IS/IS NOT NULL AND/OR another_condition AND/OR …;
Exercise
-- Find the name and role of all employees who have not been assigned to a building SELECT * FROM employees where building is null ; -- Find the names of the buildings that hold no employees SELECT * FROM buildings b left outer join employees e on e.building = b.building_name where e.building is null ;
SQL Lesson 9: Queries with expressions
-- Example query with expressions SELECT particle_speed / 2.0 AS half_particle_speed FROM physics_data WHERE ABS(particle_position) * 10.0 > 500; -- Select query with expression aliases SELECT col_expression AS expr_description, … FROM mytable; -- Example query with both column and table name aliases SELECT column AS better_column_name, … FROM a_long_widgets_table_name AS mywidgets INNER JOIN widget_sales ON mywidgets.id = widget_sales.widget_id;
Exercise
-- List all movies and their combined sales in millions of dollars SELECT m.title, (b.domestic_sales + b.international_sales) / 1000000 FROM movies m left outer join Boxoffice b on b.movie_id = m.id ; -- List all movies that were released on even number years SELECT m.title FROM movies m left outer join Boxoffice b on b.movie_id = m.id where m.year % 2 = 0 ;
SQL Lesson 10: Queries with aggregates (Pt. 1)
-- Select query with aggregate functions over all rows SELECT AGG_FUNC(column_or_expression) AS aggregate_description, … FROM mytable WHERE constraint_expression;
Exercise
-- Find the longest time that an employee has been at the studio SELECT max(years_employed) FROM employees ; -- For each role, find the average number of years employed by employees in that role SELECT role, avg(years_employed) FROM employees group by role ; -- Find the total number of employee years worked in each building SELECT building, sum(years_employed) FROM employees group by building ;
SQL Lesson 11: Queries with aggregates (Pt. 2)
-- Select query with HAVING constraint SELECT group_by_column, AGG_FUNC(column_expression) AS aggregate_result_alias, … FROM mytable WHERE condition GROUP BY column HAVING group_condition;
Did you know? If you aren't using the
GROUP BY
clause, a simpleWHERE
clause will suffice.
Exercise
-- Find the number of Artists in the studio (without a HAVING clause) SELECT count(*) FROM employees where role = 'Artist' ; -- Find the number of Employees of each role in the studio SELECT role,count(*) FROM employees group by role ; -- Find the total number of years employed by all Engineers SELECT role,sum(Years_employed) FROM employees group by role having role = 'Engineer' ;
SQL Lesson 12: Order of execution of a Query
-- Complete SELECT query SELECT DISTINCT column, AGG_FUNC(column_or_expression), … FROM mytable JOIN another_table ON mytable.column = another_table.column WHERE constraint_expression GROUP BY column HAVING constraint_expression ORDER BY column ASC/DESC LIMIT count OFFSET COUNT;
Exercise
-- Find the number of movies each director has directed SELECT m.director, count(*) FROM movies m left outer join boxoffice b on b.movie_id = m.id group by director order by director ; -- Find the total domestic and international sales that can be attributed to each director SELECT m.director, sum(domestic_sales+international_sales) FROM movies m left outer join boxoffice b on b.movie_id = m.id group by director order by director ;
SQL Lesson 13: Inserting rows
-- Insert statement with values for all columns INSERT INTO mytable VALUES (value_or_expr, another_value_or_expr, …), (value_or_expr_2, another_value_or_expr_2, …), …; -- Insert statement with specific columns INSERT INTO mytable (column, another_column, …) VALUES (value_or_expr, another_value_or_expr, …), (value_or_expr_2, another_value_or_expr_2, …), …;
SQL Lesson 14: Updating rows
-- Update statement with values UPDATE mytable SET column = value_or_expr, other_column = another_value_or_expr, … WHERE condition;
Exercise
-- The director for A Bug's Life is incorrect, it was actually directed by John Lasseter update movies set director = 'John Lasseter' where title = "A Bug's Life"
- 文字列の中でシングルクォーテーション「'」を使う場合は全体の括弧はダブルクォーテーション「"」を使う
SQL Lesson 15: Deleting rows
-- Delete statement with condition DELETE FROM mytable WHERE condition;
delete from movies where director = 'Andrew Stanton'
SQL Lesson 16: Creating tables
-- Create table statement w/ optional table constraint and default value CREATE TABLE IF NOT EXISTS mytable ( column DataType TableConstraint DEFAULT default_value, another_column DataType TableConstraint DEFAULT default_value, … );
SQL Lesson 17: Altering tables
-- Altering table to add new column(s) ALTER TABLE mytable ADD column DataType OptionalTableConstraint DEFAULT default_value; -- Altering table to remove column(s) ALTER TABLE mytable DROP column_to_be_deleted; -- Altering table name ALTER TABLE mytable RENAME TO new_table_name;
-- Add a column named Aspect_ratio with a FLOAT data type to store the aspect-ratio each movie was released in. alter talbe movies add Aspect_ratio float; -- Add another column named Language with a TEXT data type to store the language that the movie was released in. Ensure that the default for this language is English. alter table movies add Language text default 'English';
SQL Lesson 18: Dropping tables
-- Drop table statement DROP TABLE IF EXISTS mytable;
SQL Lesson X: To infinity and beyond!
これでクリアです。お疲れさまでした!
Octave 5.1.0でpauseの挙動がおかしい
CouseraのStanford University Machine Learningクラス2週目の課題ex1「2.1 Plotting the Data」において、説明に従ってplotData.mを編集し、ex1を実行すると、Enterキーを押してもpauseのまま動かなくなる事案に遭遇した。
Running warmUpExercise ... 5x5 Identity Matrix: ans = Diagonal Matrix 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 Program paused. Press enter to continue.
調べてみたところ、どうやらOctave 5.1.0ではpause関数にバグあることに原因があるようだ。
下記の記事を参考に、新たにpause関数を定義することで解決した。
octave pause; not working properly. Forces to ctrl+c :( : octave
ex1.mの適当な箇所に次の内容を追記。
function pause input('','s'); end
これでEnterキーを押すごとに処理が進行する。
Rubyで変数が定義されているか確認する
defined?
Rubyで変数が定義されているか確認するにはdefined?
メソッドを使用する。defined?
は引数として入れた変数やメソッドが定義済みであれば式の種別を表す文字列を返す。未定義であればnil
を返す。
例
>> a = 1 >> defined? a => "local-variable" >> hoge = nil >> defined? hoge => "local-variable" >> defined? poke => nil >> defined? puts => "method" >> defined? defined? Traceback (most recent call last): SyntaxError ((irb):10: syntax error, unexpected end-of-input) defined? defined? ^
参考
ControllerのActionが表示するページごとにSCSSを設定する | Rails
app/views/layouts/application.html.erbのbodyタグにERBで次のように記述する。
<body class='<%= "#{controller.controller_name}_#{controller.action_name}" %>'>
こうしておくと、たとえばStaticPagesControllerのHomeアクションのViewファイル「home.html.erb」に対してclass="static_pages_home"
とクラスが付帯される。よって、このアクションが表示するページをSCSSで修飾したい場合はSCSSファイルにおいて
.static_pages_home {}
という形で括弧の中を編集すれば良い。
リンクを新規タブで開かせるにはaタグにtarget=”_blank”属性をつける
例 Sayf
<a href="https://sayf-fictional-sayings.herokuapp.com/" target=”_blank”>Sayf</a>
Railsアプリに包含・除外検索機能を実装する
目次
はじめに
Twitter似のRailsアプリに投稿検索機能を実装したので、この折に自分の理解した内容をまとめます。
実装する検索機能は包含検索(検索語が含まれる曖昧検索)、除外検索(先頭にマイナス記号(-)を付けた検索語が含まれない曖昧検索)で、複数の単語を入れる場合は間にスペースを入力するものとします。
曖昧検索とは、検索語を少なくとも一部に含む要素を抽出する検索方式のことです。部分一致検索とも呼びます。
前提
検索メソッド
contentカラムにtextデータが入ったPostモデルを想定します。
対象のモデルに次のように検索用のクラスメソッドを定義します。
app/models/post.rb
def self.search_with(keywords) return none if keywords.blank? keywords = keywords.split(/[[:blank:]]+/).select(&:present?) excluding_terms, including_terms = keywords.partition { |keyword| keyword.start_with?('-') && keyword.length >= 2 } | including_terms = [including_terms.map { | term | "%#{term}%" }.join] | | excluding_terms = [excluding_terms.map { | term | "%#{term.delete_prefix('-')}%" }.join] | return Saying.where('content NOT LIKE ?', excluding_terms) if including_terms.select(&:present?).empty? Post.where('content LIKE ?', including_terms) .where('content NOT LIKE ?', excluding_terms) end
nil回避のためのreturn none
return none if keywords.blank?
は検索欄に単語が入力されていない状態で検索が実行された場合に早期リターンするための一文です。ここで注意するのはreturn if keywords.blank?
ではなく、none
を返しているという点です。これによりコントローラーでページングに使うpagyメソッドの引数が、検索結果0件の場合でもnilではなく#<ActiveRecord::Relation []>
となるようにしています。
複数の検索語を配列に格納する
keywords = keywords.split(/[[:blank:]]+/).select(&:present?)
について。
split(/[[:blank:]]+/)
:スペース区切りで各単語を配列の要素として格納するselect(&:present?)
:配列からnilと空文字列("")以外の要素を抽出する(reject(&:blank?)
でも同じ結果が得られる)
使用例からわかるように、"''"
は残ってしまうことに注意です。
包含検索語と除外検索語を別々の配列に格納する
excluding_terms, including_terms = keywords.partition { |keyword| keyword.start_with?('-') && keyword.length >= 2 }
上のコードは、keywordsの要素のうちマイナス記号から始まる2文字以上の要素をexcluding_termsに、それ以外をincluding_termsに格納しています。
a, b = enum.partition {|item| block }
:block
の戻り値が真の要素を左辺第一項の配列に、偽の要素を左辺第二項の配列に格納するstring.start_with?
:stringオブジェクトの中身が引数の文字列から始まるならtrue、それ以外はfalseを返す
検索語を含んだ配列を曖昧検索の形式に合わせて整形する
この節が本記事の肝となる部分です。 まずRailsにおける曖昧検索の形式を以下に示します。
# 検索語が単数の場合 モデル.where("カラム名 LIKE '%検索語%'") # 検索語が複数の場合 モデル.where("カラム名 LIKE", ['%検索語1%%検索語2%...%検索語N%'])
検索語が複数の場合、条件は要素数1のstring配列となることに注意してください。この形式に適合するように、各検索語を%で囲み結合した文字列の配列に整形します。
| including_terms = [including_terms.map { | term | "%#{term}%" }.join] | | excluding_terms = [excluding_terms.map { | term | "%#{term.delete_prefix('-')}%" }.join] |
array.map {|item| block }
:要素の数だけblockを実行し、その戻り値を集めた配列を返すjoin
:引数に指定した文字列を区切りに要素を結合した文字列を返すdelete_prefix
:引数に指定した接頭語を削除する
where likeで曖昧検索
postsテーブルのcontentカラムのうち、including_terms
内の文字列を含んでいるものを抽出し、そこからexcluding_terms
内の文字列を含んでいるものを除外します。
return Post.where('content NOT LIKE ?', excluding_terms) if including_terms.select(&:present?).empty? Post.where('content LIKE ?', including_terms) .where('content NOT LIKE ?', excluding_terms)
return文の箇所は検索語にマイナス記号の接頭辞が付いたものしかない場合の処理です。
その場合、including_termsは[""]
であり、Post.where('content LIKE ?', including_terms)
の戻り値は#<ActiveRecord::Relation []>
となります。つまり、検索結果は該当0件です。ここではPost全体から除外検索語を含まないものを抽出したいので、上述のようなreturn文の分岐を入れているわけです。
実行例
定義の流れに沿ってコードを実行した例を下に載せておきます。
例1:検索結果0件
>> keywords = " '' - -- love -you" => " '' - -- love -you" >> keywords.split(/[[:blank:]]+/) => ["", "''", "-", "--", "love", "-you"] >> keywords = keywords.split(/[[:blank:]]+/).select(&:present?) => ["''", "-", "--", "love", "-you"] >> excluding_terms, including_terms = keywords.partition { |keyword| keyword.start_with?('-') && keyword.length >= 2 } => [["--", "-you"], ["''", "-", "love"]] >> including_terms = [including_terms.map { |term| "%#{term}%" }.join] => ["%''%%-%%love%"] >> excluding_terms = [excluding_terms.map { |term| "%#{term.delete_prefix('-')}%" }.join] => ["%-%%you%"] >> Post.where('content LIKE ?', including_terms).where('content NOT LIKE ?', excluding_terms) Post Load (2.2ms) SELECT "posts".* FROM "posts" WHERE (content LIKE '%''''%%-%%love%') AND (content NOT LIKE '%-%%you%') ORDER BY "posts"."created_at" DESC LIMIT ? [["LIMIT", 11]] => #<ActiveRecord::Relation []>
例2:検索結果1件
>> keywords = " -love , ' you " => " -love , ' you " >> keywords = keywords.split(/[[:blank:]]+/).select(&:present?) => ["-love", ",", "'", "you"] >> excluding_terms, including_terms = keywords.partition { |keyword| keyword.start_with?('-') && keyword.length >= 2 } => [["-love"], [",", "'", "you"]] >> including_terms = [including_terms.map { |term| "%#{term}%" }.join]=> ["%,%%'%%you%"] >> excluding_terms = [excluding_terms.map { |term| "%#{term.delete_prefix('-')}%" }.join] => ["%love%"] >> Post.where('content LIKE ?', including_terms).where('content NOT LIKE ?', excluding_terms) Post Load (3.9ms) SELECT "posts".* FROM "posts" WHERE (content LIKE '%,%%''%%you%') AND (content NOT LIKE '%love%') ORDER BY "posts"."created_at" DESC LIMIT ? [["LIMIT", 11]] => #<ActiveRecord::Relation [#<Post id: 38, content: "Goodnight, my darlings, I'll see you tomorrow.", user_id: 3, created_at: "2019-08-18 15:09:05", updated_at: "2019-08-22 03:09:05", picture: nil>
以上で検索メソッドについての説明はおしまいです。
Routes
URLが/posts/searchとなるようにcollectionを使ってsearchアクションへのルーティングを行います。
config/routes.rb
resources :posts, only: %i[create destroy] do collection do get 'search' end end
Controller
検索フォームから送信されたキーワードをモデルの検索メソッドの引数として処理させます。その戻り値をページングした上でView側に渡すためのインスタンス変数に格納します。
app/controllers/posts_controller.rb
def search return if params[:keywords].blank? @keywords = params[:keywords] @pagy_results, @search_results = pagy(post.search_with(@keywords), items: 30) respond_to do |format| format.html { redirect_back(fallback_location: request.referer) } format.js end end
検索フォーム
routesで設定した検索アクションのパスをform_with
のurlにセットします。text_fieldに:keywordsを設定することも忘れずに!
app/views/posts/_search_posts_form.html.erb
<%= form_with url: search_posts_path, method: :get do |f| %> <%= f.text_field :keywords, placeholder: "Search", class: "form-control" %> <%= f.submit value="🔍".html_safe, class: "btn btn-default" %> <% end %> <% if @search_results %> <div class="search_results"> <h5>Search Terms: <%= @keywords %></h5> <ol class="posts"> <%= render @search_results %> </ol> <%== pagy_bootstrap_nav(@pagy_results) if @pagy_results.pages > 1 %> </div> <% end %>
app/views/home.html.erb
<div id="search_posts"> <%= render 'posts/search_posts_form' %> </div>
ページを再読み込みしなくても検索結果が表示されるようにコントローラーのアクションと同じ名前のjs.erbファイルを用意し、次のように編集します。
app/views/posts/search.js.erb
$("#search_posts").html("<%= j(render 'posts/search_posts_form') %>");
SCSSでdisplay: inline-flex
を指定すると、検索欄と検索ボタンが横並びに表示されます。
app/assets/stylesheets/custom.css.scss
#search_posts { form { display: inline-flex; width: 100%; margin-top: 20px; margin-bottom: 10px; input[type="text"] { margin: 0 0 0 0; } input[type="submit"] { padding: 5px; } } h5 { text-align: left; } }
テスト
統合テスト
まずテスト用のfixtureファイルを適当に用意します。
test/fixtures/posts.yml
one: content: "ぼくの青春はぼくから逃れてゆく。病気というのはそのことだ。 -- アルベール・カミュ『手帖』" created_at: <%= 10.minutes.ago %> user: john two: content: "光陰虚しく渡ることなかれ -- 道元『正法眼蔵随聞記』" created_at: <%= 20.minutes.ago %> user: john most_recent: content: "This is the most recent post." created_at: <%= Time.zone.now %> user: john ants: content: "Oh, is that what you want? Because that's how you get ants!" created_at: <%= 2.years.ago %> user: user_0
rails g integration_test search
で統合テストを作成し、次のように編集しました。
test/integration/search_test.rb
require 'test_helper' class SearchTest < ActionDispatch::IntegrationTest def setup @existent_valid_terms = ['-', '--', '---', '-is', 'this is -- ', '『', '\'', '-,'] @non_existent_valid_terms = ['くぁwせdrftgyふじこlp', 'WTF!?', '\n'] @invalid_terms = ['', ' ', nil] end test 'should display posts after searching with existent valid terms' do @existent_valid_terms.each do |term| get root_path assert_select 'div#search_posts' assert_no_match 'search_results', @response.body get search_posts_path, xhr: true, params: { keywords: term } assert_response :success assert_match 'search_results', @response.body assert_match 'post', @response.body assert_select 'li' end end test 'should not display any posts after searching with non-existent valid terms' do @non_existent_valid_terms.each do |term| get root_path assert_no_match 'search_results', @response.body get search_posts_path, xhr: true, params: { keywords: term } assert_response :success assert_match 'search_results', @response.body assert_select 'li', false end end test 'should not display search results after searching with invalid terms' do @invalid_terms.each do |term| get root_path assert_select 'div#search_posts' assert_no_match 'search_results', @response.body get search_posts_path, xhr: true, params: { keywords: term } assert_response :success assert_no_match 'search_results', @response.body end end end
setup
を見るとわかるように
@existent_valid_terms
には検索語として有効で検索結果に該当するものがある語を、
@non_existent_valid_terms
には検索語として有効で検索結果に該当するものがない語を、
@invalid_terms
には検索語として無効な語が格納されます。
このテストでは、setupで定義したそれぞれの配列を使って検索したときに期待する結果が@response.body
に含まれていることや投稿を一覧で表示するリストのタグ<li>
の有無を確認しています。
実際に検索してみた様子
次に示す表の検索語を使って画面から実際に検索をかけてみます。
検索語 | 期待する検索結果 |
---|---|
ice | 「ice」を含む投稿 |
-cream | 「cream」を含まない投稿 |
ice-cream | 「ice-cream」を含む投稿 |
ice -cream | 「ice」を含む、かつ「cream」を含まない投稿 |
包含・除外検索の動作チェック pic.twitter.com/YXAZ7p0SJp
— 変数c (@var_crn) 2019年8月29日
無事、期待する検索結果を得ることができました。
おわりに
もっとエレガントでセキュアなやり方があれば是非教えてください。