buq’s blog

覚えておきたいけど覚えておけなさそうなことを書きます?

NULL と陽に書いていない空なカラムを NULL として取り込ませる @ mysql

会社の人に mysqlcsvを読み込むとき,NULL と陽に書いていない空なカラムを NULL として取り込ませる方法を教えて貰った.メモ.

drop table if exists foo;                                                                                                               
create table foo (                                                                                                                      
  a INTEGER default null,                                                                                                               
  b INTEGER default null                                                                                                                
);                                                                                                                                      
                                                                                                                                        
LOAD DATA LOCAL INFILE 'dat.csv'                                                                                                        
-- <dat.csv>                                                                                                                            
-- 1,2                                                                                                                                  
-- 3,                                                                                                                                   
-- ,4                                                                                                                                   
-- ,                                                                                                                                    
-- </dat.csv>                                                                                                                           
INTO TABLE foo                                                                                                                          
FIELDS TERMINATED BY ','                                                                                                                
(a, @b_raw)                                                                                                                             
set b = case @b_raw when '' then NULL else @b_raw end;                                                                                  
                                                                                                                                        
select * from foo;                                                                                                                      

カラム b は一度生データを b_raw という変数で受けて,これが空文字列なら b に NULL をセットするようにしている.a は何も処理していない.

結果:

+------+------+
| a    | b    |
+------+------+
|    1 |    2 |
|    3 | NULL |
|    0 |    4 |
|    0 | NULL |
+------+------+

🎉

k番目要素

数列が与えられ,k番目の要素を求めるという問題がある.

解き方としてクイックソートに類似したものがあり,計算時間の期待値は O(n) である.

参考:Spaghetti Source - k 番目の要素の選択

この問題をとく乱択でないアルゴリズムをしった(上記サイトにも解説がある)ので,練習のため実装した. クイックソートライクのアルゴリズムと似ているが,ピボット選択が悪くならないように工夫をしている.

以下コード.ここではkを0はじまりとしている.

#include <cassert>
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <numeric>

template <class T>
T calc_k_th_smallest_elem(const std::vector<T>& v, size_t k){
  // k in [0, #v)
  assert(k<v.size());

  // Base case, #v <= 10
  if(v.size() <= 10){
    auto v_copied(v);
    std::sort(v_copied.begin(), v_copied.end());
    return v_copied[k];
  }

  // #v > 10
  std::vector<T> mids;
  mids.reserve(v.size()/5);
  for(size_t i=0; i<v.size()/5; i+=5){
    std::vector<T> mid_candidates;
    for(size_t j=0; j<5; ++j){mid_candidates.push_back(v[i+j]);}
    sort(mid_candidates.begin(), mid_candidates.end());
    mids.push_back(mid_candidates[2]);
  }

  T m = calc_k_th_smallest_elem(mids, mids.size()/2);
  std::vector<T> lt_m;
  std::copy_if(v.begin(), v.end(), std::back_inserter(lt_m), [&m](const T& x)->bool{return x<m;});
  std::vector<T> eq_m;
  std::copy_if(v.begin(), v.end(), std::back_inserter(eq_m), [&m](const T& x)->bool{return x==m;});
  std::vector<T> gt_m;
  std::copy_if(v.begin(), v.end(), std::back_inserter(gt_m), [&m](const T& x)->bool{return x>m;});

  assert(gt_m.size() + eq_m.size() + lt_m.size() == v.size());

  if(lt_m.size() > k){
    return calc_k_th_smallest_elem(lt_m, k);
  }
  else if (lt_m.size() + eq_m.size() > k){
    return m;
  }
  else{
    return calc_k_th_smallest_elem(gt_m, k - lt_m.size() - eq_m.size());
  }
}

clang -O2 でコンパイルすると, n = 1億, T = int で6+秒の実行時間でした. Spaghetti Source - k 番目の要素の選択 にもあるように,クイックソート版より実装は複雑である.が,C++11の恩恵もあって,それなりに読める長さになっていると思う.特にラムダ式とcopy_ifが貢献している…気がします.

このコードは「データ構造とアルゴリズム(杉原)」の本アルゴリズムの解説をもとに書き起こしたものです.

データ構造とアルゴリズム

データ構造とアルゴリズム

ipython notebook のファイルをダブルクリックで開く:訂正記事

ipython notebook のファイルをダブルクリックで開く方法について以前かいた記事のコードが大幅に間違っていたので訂正します.当該ページは訂正済み.

以前下記の記事を書きました: buq.hateblo.jp

ここで,Automatorシェルスクリプトとして下記のものを使うと書きましたが, この前半(8902ポートの使用状態をみて,必要ならipythonのサーバをたてる) が甚だしく間違っていました.

これで行けると思ってしまったのは,サーバがたった状態で動作を確認していたからです.戸惑わせてしまった方,申し訳ありません😩

正しいコードはたとえば下記のようなものになります:

PORTSTAT_IPYNB=`netstat -tln | grep "8902" | wc -l`
if [ "$PORTSTAT_IPYNB" -eq "0" ]
  then
    bash -cl 'ipython notebook / --no-browser --port=8902 & sleep .5' > /dev/null 2>&1 
    sleep .5
fi
for f in "$@"
do
  open http://localhost:8902/notebooks"$f"
done

解説: 1. PORTSTAT_IPYNB=`netstat -tln | grep "8902" | wc -l` 8902 の使用状態を調べる.0だったらipythonサーバがたっていないとみなして立てる. 2. bash -cl 'ipython notebook / --no-browser --port=8902 & sleep .5' > /dev/null 2>&1Automator から直接叩くと怒られるので,bash を立ち上げて,その中で ipython をたちあげる.> /dev/null 2>&1 はなくても動くが,その場合なぜかAutomatorが「プロセスが終わらないよ〜」とずっと歯車アイコンをくるくるさせる.それがうっとおしいので > /dev/null 2>&1 で黙らせる. 参考;Automator workflow "run shell script" keeps run... | Apple Support Communities

整数の除算 @ C++

a/b とか a%b の挙動についてメモ. a, b の一方が負である場合は処理系依存C/C++のバージョンにもよる?)で, 多くの処理系で「実数の割り算の値a/bを0方向に丸める」ということらしい. ただし (a/b)*b + a%b == a は成り立つようになっている.

先日書いた 関数オブジェクト @ C++ - buq’s blog について考えていて気になったので.(こちらのコードも修正した)

参考 : - Integer division rounding with negatives in C++ - Stack Overflow - BohYoh.com-C/C++ FAQ 負数に対する除算の結果がおかしいのはどうしてですか。

以下実験コード

#include <iostream>
#include <iomanip>
#include <vector>
#include <numeric>
#include <algorithm>
#define range(container) container.begin(), container.end()
#define say(smth) std::cout << smth << std::endl

using namespace std;

typedef int (*func_type) (int, int);

int mymod(int a, int b){ return a%b; }            
int mydiv(int a, int b){ return a/b; }             // func. name div occupied by C language
int mynum(int a, int b){ return (a/b) * b + a%b; } // returns numerator

void show_func_result_table(const vector<int> as, const vector<int> bs, func_type f){
  cout << "b\\a|";
  for(auto && a : as) cout << setw(3) << a;
  cout << endl;
  cout << "====";
  for(auto && a : as) cout << "===";
  cout << endl;
  for(auto && b : bs){
    //if(b == 0) continue;
    cout << setw(3) << b << "|";
    for(auto && a : as) cout << setw(3) << f(a,b);
    cout << endl;
  }
}

int main(){
  const int n = 10;
  vector<int> a(n), b(n);
  //iota(a.begin(), a.end(), -4);
  iota(range(a), -4);
  auto mid_b = b.begin() + (b.end()-b.begin())/2; 
  iota(b.begin(), mid_b, -(n/2));
  iota(mid_b, b.end(), 1);

  say("a\%b");
  show_func_result_table(a, b, mymod);
  
  say("----------------------------------");
  say("a/b");
  show_func_result_table(a, b, mydiv);

  say("----------------------------------");
  say("a");
  show_func_result_table(a, b, mynum);
    
  return 0;
}

結果:

bash-3.2$ g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 6.1.0 (clang-602.0.53) (based on LLVM 3.6.0svn)
Target: x86_64-apple-darwin14.4.0
Thread model: posix
bash-3.2$ g++ ./main.cpp -std=c++11 -o main.out
bash-3.2$ ./main.out 
a%b
b\a| -4 -3 -2 -1  0  1  2  3  4  5
==================================
 -5| -4 -3 -2 -1  0  1  2  3  4  0
 -4|  0 -3 -2 -1  0  1  2  3  0  1
 -3| -1  0 -2 -1  0  1  2  0  1  2
 -2|  0 -1  0 -1  0  1  0  1  0  1
 -1|  0  0  0  0  0  0  0  0  0  0
  1|  0  0  0  0  0  0  0  0  0  0
  2|  0 -1  0 -1  0  1  0  1  0  1
  3| -1  0 -2 -1  0  1  2  0  1  2
  4|  0 -3 -2 -1  0  1  2  3  0  1
  5| -4 -3 -2 -1  0  1  2  3  4  0
----------------------------------
a/b
b\a| -4 -3 -2 -1  0  1  2  3  4  5
==================================
 -5|  0  0  0  0  0  0  0  0  0 -1
 -4|  1  0  0  0  0  0  0  0 -1 -1
 -3|  1  1  0  0  0  0  0 -1 -1 -1
 -2|  2  1  1  0  0  0 -1 -1 -2 -2
 -1|  4  3  2  1  0 -1 -2 -3 -4 -5
  1| -4 -3 -2 -1  0  1  2  3  4  5
  2| -2 -1 -1  0  0  0  1  1  2  2
  3| -1 -1  0  0  0  0  0  1  1  1
  4| -1  0  0  0  0  0  0  0  1  1
  5|  0  0  0  0  0  0  0  0  0  1
----------------------------------
a
b\a| -4 -3 -2 -1  0  1  2  3  4  5
==================================
 -5| -4 -3 -2 -1  0  1  2  3  4  5
 -4| -4 -3 -2 -1  0  1  2  3  4  5
 -3| -4 -3 -2 -1  0  1  2  3  4  5
 -2| -4 -3 -2 -1  0  1  2  3  4  5
 -1| -4 -3 -2 -1  0  1  2  3  4  5
  1| -4 -3 -2 -1  0  1  2  3  4  5
  2| -4 -3 -2 -1  0  1  2  3  4  5
  3| -4 -3 -2 -1  0  1  2  3  4  5
  4| -4 -3 -2 -1  0  1  2  3  4  5
  5| -4 -3 -2 -1  0  1  2  3  4  5

-1%2は 1 になって欲しいなあ😩

関数オブジェクト @ C++

C++には関数オブジェクトというものがある. class に operator()を適当に定義してやれば作ることができ, これを用いるとパラメタつき関数みたいなものが実現できる.

ここではパラメタつき比較関数(二つの引数をとって,前者が後者より厳密に小さいときtrueを返す )を実装している.この比較関数は int 型の「大小」を比較するもので, パラメタeven_regarded_smallertrueであれば,偶数は奇数より小さいと判断され,偶奇が一致するものは普通の大小関係で比較される.false であれば奇数が偶数より小さいと判断され,偶奇が一致するものは普通の大小関係で比較される.

下記のコードはC++11で書かれているので, 古いコンパイラで怒られるようであれば適当な書き換えが必要.

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>

#define say(smth) std::cout << smth << std::endl

using namespace std;

class ParityFirstComp{
  //static   
public:
  static bool is_even(const int& n) {return n%2==0;}
  static bool is_odd (const int& n) {return n%2!=0;} //n%2 can be -1
  static bool have_same_parity(const int& m, const int& n){return (n-m)%2==0;}

  //non-static
private:  
  bool even_regarded_smaller;
public:
  ParityFirstComp(bool even_regarded_smaller):
    even_regarded_smaller(even_regarded_smaller)
  {}
  
  bool compare_by_parity(const int &l, const int& r) const{
    if(even_regarded_smaller)
      return is_even(l) && is_odd (r);
    else
      return is_odd (l) && is_even(r);
  }
  bool operator()(const int &l, const int& r) const{
    if(have_same_parity(l, r))
      return l < r;
    else
      return compare_by_parity(l, r);
  }
};

template<class T>
ostream& operator<< (ostream& strm, const vector<T>& v){
  for(auto&& elm : v) strm << elm << ", ";
  return strm;
}

int main(){
  vector<int> v(10);
  iota(v.begin(), v.end(), -4);
  random_shuffle(v.begin(), v.end());
  vector<int> u(v);

  say("original:");
  say(v);

  say("even_regarded_smaller:");
  ParityFirstComp even_first_comp(true);
  sort(v.begin(), v.end(), even_first_comp);
  say(v);

  say("odd_regarded_smaller:");
  ParityFirstComp odd_first_comp(false);
  sort(u.begin(), u.end(), odd_first_comp);
  say(u);

  return 0;
}

出力は次のようになる.(未ソートの結果はrandom_shuffleの実装に依存すると思う)

original:
6, 0, 3, 5, 7, 8, 4, 1, 2, 9, 
even_regarded_smaller:
0, 2, 4, 6, 8, 1, 3, 5, 7, 9, 
odd_regarded_smaller:
1, 3, 5, 7, 9, 0, 2, 4, 6, 8, 

それっぽい挙動をしている.

2015/8/23 負の整数にも対応できるよう is_odd を書き換えた.

ipython notebook のファイルをダブルクリックで開く @ OS X

ipython notebook の .ipynb ファイルをダブルクリックで開きたい人がいます(私です).

他のひともそういうことを考えているみたいです👸

osx mountain lion - Open an ipython notebook via double-click on osx - Stack Overflow

上記のリンクでは Ubuntu ようのシェルスクリプトがのっていますが, OS X の Finder はシェルスクリプトに優しくないのでこのままでは使えません💢✋

この記事では Finder と 上記のスクリプトをつなぐ glue を OS X に付属する Automator で作る…というのをやります😩

やりかた

1. Automator を起動 & 新規ファイルで Application をえらぶ f:id:buq:20150819214227p:plain

2. run shell script を左上の検索欄からしらべる f:id:buq:20150819214329p:plain

3. このアクション(というのかな?)を右の Application receives files and folders as input というところの下にドラッグしていきます.勝手に繋がります👺👍✨f:id:buq:20150819214908p:plain 右上の pass input: というところを as arguments にしておきます.

4. シェルスクリプトを書きます.このスクリプトosx mountain lion - Open an ipython notebook via double-click on osx - Stack Overflow を改変したものです.ここからはただのシェルスクリプトなので,好きなことを書けばいいです👮 アプリとして適当な場所に保存します.

PORTSTAT_IPYNB=`netstat -tln | grep "8902" | wc -l`
#echo "$PORTSTAT_IPYNB" > ~/Desktop/hoge.txt
if [ "$PORTSTAT_IPYNB" -eq "0" ]
  then
    #bash -cl 'ipython notebook / --no-browser --port=8902 & sleep .5' > /dev/null 2>&1  &
    bash -cl 'ipython notebook / --no-browser --port=8902 & sleep .5' > /dev/null 2>&1 
    sleep .5
fi
for f in "$@"
do
  open http://localhost:8902/notebooks"$f"
done

xdg-openopen になってるのは OS X だからです.他には,forループが増えています.それだけです👍 2015/08/30 Automator で作ったアプリから ipython のサーバを起動させるためには,ipython notebook を直接叩くのではなく,bash -cl 'ipython notebook / --no-browser --port=8902 & sleep .5' > /dev/null 2>&1 とせねばならないようです.またif文も間違っていました.これで動いていたような気になっていたのは,すでにサーバが立っている状態で動作を確認していたからです.戸惑わせてしまった方,申し訳ありません.

5. hoge.ipynb とかを適当に選んで右クリック→ Get info で Open with で先ほどのアプリを選びます. info ウィンドウで Change All... を選べばOKです👺👺👺 f:id:buq:20150819215543p:plain

間違いなど発見したらご連絡ください💫

このテクニックはシェルスクリプトOS X上で普通のアプリケーションみたいに動かすために広く使えそう.