任意の文字に基づいて可能な限りすべての単語を生成するには?

How to generate all words possible with somes caracters? – StackOverflow

「単語」を文字通り解釈すれば,コーパスを考える必要があるけど,”to generate all possible combinations”とあるので,単に組み合わせの問題らしい.例えば,cartesian product(デカルト積)を求めたい場合は,

from itertools import product


lst = 'U,A,N'.split(',')
[''.join(tupl) for tupl in product(lst, repeat=3)]
['UUU',
 'UUA',
 'UUN',
 'UAU',
 'UAA',
 'UAN',
 'UNU',
 'UNA',
 'UNN',
 'AUU',
 'AUA',
 'AUN',
 'AAU',
 'AAA',
 'AAN',
 'ANU',
 'ANA',
 'ANN',
 'NUU',
 'NUA',
 'NUN',
 'NAU',
 'NAA',
 'NAN',
 'NNU',
 'NNA',
 'NNN']

というのが答えになるが,これは,Bashで物凄く簡単に求める事ができて,

%%bash
time {
echo {U,A,N}{U,A,N}{U,A,N}
}
UUU UUA UUN UAU UAA UAN UNU UNA UNN AUU AUA AUN AAU AAA AAN ANU ANA ANN NUU NUA NUN NAU NAA NAN NNU NNA NNN

real    0m0.000s
user    0m0.000s
sys 0m0.000s

となる.順列の場合,

from itertools import permutations


lst = 'U,A,N'.split(',')
[''.join(tupl) for tupl in permutations(lst)]
['UAN', 'UNA', 'AUN', 'ANU', 'NUA', 'NAU']

というのが解になるが,Bashではどの様にして求めるのが良いんだろうか.
例えば,デカルト積を求めた後で,grepで絞っていって順列を求めるという方針でいけば,

%%bash
time {
echo {U,A,N}{U,A,N}{U,A,N} | tr ' ' '\n' | grep 'U' | grep 'A' | grep 'N'
}
UAN
UNA
AUN
ANU
NUA
NAU

real    0m0.010s
user    0m0.008s
sys 0m0.010s

重複がなければこれでいいが,重複がある場合はsortしてuniqする必要がある.かつ,上記の様に,各単語を固有としてみなすのではなく,生成後文字列でユニーク値のみが残る.

%%bash
time {
echo {K,K,K,U,A,N}{K,K,K,U,A,N}{K,K,K,U,A,N}{K,K,K,U,A,N}{K,K,K,U,A,N}{K,K,K,U,A,N} | tr ' ' '\n' | grep 'K.*K.*K' | grep 'U' | grep 'A' | grep 'N' | sort | uniq >res.txt
}
real    0m0.048s
user    0m0.049s
sys 0m0.023s
import pandas as pd


lst = 'K,K,K,U,A,N'.split(',')
res = list(set(''.join(tupl) for tupl in permutations(lst)))

df1 = pd.DataFrame(res)
df2 = pd.read_csv('res.txt', header=None)
df = pd.concat((df1, df2), 1)

pd.testing.assert_series_equal(
    df.iloc[:, 0].sort_values().reset_index(drop=True), 
    df.iloc[:, 1].sort_values().reset_index(drop=True)
)
print(df.describe())
             0       0
count      120     120
unique     120     120
top     KKKAUN  KKKAUN
freq         1       1
広告
カテゴリー: 未分類 | コメントをどうぞ

行内の各値を各行の合計値で置き換える方法は?

How to sum each row and replace each value in row with sum? – StackOverflow

回答の方法がリーズナブルチョイスだけど,masked arrayとしても考える事ができる.

import io
import pandas as pd
import numpy as np


strings = """   1  2  3  4  5
 1 2  4  5 NaN 3
 2 3  5  6  1  2
 3 3  1  1  1  1"""
df = pd.read_csv(io.StringIO(strings), sep='\s+')
print(df)

print(df.notna().replace(False, np.nan).mul(df.sum(1), 0))
%timeit df.notna().replace(False, np.nan).mul(df.sum(1), 0)

print(df.where(df.isna(), df.sum(1), axis=0))
%timeit df.where(df.isna(), df.sum(1), axis=0)

arr = df.values
a = np.broadcast_to(np.nansum(arr, 1)[:, None], df.shape)
res = np.where(np.isnan(arr), np.nan, a)
print(pd.DataFrame(res, index=df.index, columns=df.columns))
%timeit arr = df.values;a = np.broadcast_to(np.nansum(arr, 1)[:, None], df.shape);res = np.where(np.isnan(arr), np.nan, a);pd.DataFrame(res, index=df.index, columns=df.columns)

df = pd.concat([df]*100000, ignore_index=True)
%timeit df.notna().replace(False, np.nan).mul(df.sum(1), 0)
%timeit df.where(df.isna(), df.sum(1), axis=0)
%timeit arr = df.values;a = np.broadcast_to(np.nansum(arr, 1)[:, None], df.shape);res = np.where(np.isnan(arr), np.nan, a);pd.DataFrame(res, index=df.index, columns=df.columns)
   1  2  3    4  5
1  2  4  5  NaN  3
2  3  5  6  1.0  2
3  3  1  1  1.0  1
    1   2   3    4   5
1  14  14  14  NaN  14
2  17  17  17   17  17
3   7   7   7    7   7
1000 loops, best of 3: 1.37 ms per loop
    1   2   3     4   5
1  14  14  14   NaN  14
2  17  17  17  17.0  17
3   7   7   7   7.0   7
100 loops, best of 3: 2.99 ms per loop
      1     2     3     4     5
1  14.0  14.0  14.0   NaN  14.0
2  17.0  17.0  17.0  17.0  17.0
3   7.0   7.0   7.0   7.0   7.0
10000 loops, best of 3: 148 µs per loop
1 loop, best of 3: 174 ms per loop
10 loops, best of 3: 130 ms per loop
100 loops, best of 3: 17.7 ms per loop

「pandas.DataFrame.where/pandas.DataFrame.mask」は効率があまり良くないので,numpy.whereに置き換える方がリーズナブル.

今の場合は,こうする意味はあまり無いと思うが,masked arrayとして考える事もできる.

df = pd.read_csv(io.StringIO(strings), sep='\s+')
arr = df.values
m = np.isnan(arr)
a = np.ma.array(arr, mask=m)
res = np.broadcast_to(a.sum(1)[:, None], arr.shape, subok=True)
res.mask = m
print(pd.DataFrame(res, index=df.index, columns=df.columns))
%timeit arr = df.values;m = np.isnan(arr);a = np.ma.array(arr, mask=m);res = np.broadcast_to(a.sum(1)[:, None], arr.shape, subok=True);res.mask = m;pd.DataFrame(res, index=df.index, columns=df.columns)

df = pd.concat([df]*100000, ignore_index=True)
%timeit arr = df.values;m = np.isnan(arr);a = np.ma.array(arr, mask=m);res = np.broadcast_to(a.sum(1)[:, None], arr.shape, subok=True);res.mask = m;pd.DataFrame(res, index=df.index, columns=df.columns)
      1     2     3     4     5
1  14.0  14.0  14.0   NaN  14.0
2  17.0  17.0  17.0  17.0  17.0
3   7.0   7.0   7.0   7.0   7.0
1000 loops, best of 3: 376 µs per loop
10 loops, best of 3: 38.8 ms per loop
df = pd.concat([df]*1000000, ignore_index=True)
arr = df.values
%timeit m = np.isnan(arr);a = np.ma.array(arr, mask=m);res = np.broadcast_to(a.sum(1)[:, None], arr.shape, subok=True);res.mask = m
%timeit a = np.broadcast_to(np.nansum(arr, 1)[:, None], arr.shape);res = np.where(np.isnan(arr), np.nan, a)
%timeit res = np.broadcast_to(np.nansum(arr, 1)[:, None], arr.shape).copy();res[np.isnan(arr)] = np.nan;pd.DataFrame(res, index=df.index, columns=df.columns)
1 loop, best of 3: 286 ms per loop
10 loops, best of 3: 160 ms per loop
1 loop, best of 3: 220 ms per loop

ここで興味深いのは,「numpy.nansum」が非常に効率的な関数だという事だろう.以下は,「numpy.nanmux」に関するパフォーマンスを表しているが,

Find the max of two or more columns with pandas – StackOverflow

「numpy.nanmux」と「numpy.max」はほぼ同じパフォーマンスを示している.NaN(numpy.nan)の処理について,自分であれこれブーリアンを考えるよりも,関数に投げてしまった方がリーズナブルなのが分かる.逆に云えば,(行x列)=(300万x5)でその差は100 ms程度なので,複雑なブーリアン処理に於いて,masked arrayはベネフィットが大きい事が分かる.

カテゴリー: 未分類 | コメントをどうぞ

csvファイルを配列に変換して比較し、それを別のcsvファイルのエントリと置き換える方法は? – Awk

How to read a csv file into arrays and comapre and replace it with entries from another csv file? – StackOverflow

言い換えれば,2つのファイルを補完的に結合する,2つのデータフレームに対して,インデックスとカラムが和集合となる様なデータフレームを作成して,1つ目のファイルをベースに値が存在しない場合は2つ目の値で補完する様な処理を考える.言葉にするとよく分からないけど,pandas.DataFrame.combine_firstのような処理.

%%writefile file1
Header1,Header2,Header3,Header4
aaaaaaa,bbbbbbb,ccccccc,ddddddd
eeeeeee,fffffff,ggggggg,hhhhhhh
iiiiiii,jjjjjjj,kkkkkkk,lllllll
mmmmmmm,nnnnnnn,ooooooo,ppppppp
%%writefile file2
Header1,Header2,Header3
aaaaaaa,cat,dog
iiiiiii,doctor,engineer
mmmmmmm,sky,blue
import pandas as pd


df1 = pd.read_csv('file2', index_col=0)
df2 = pd.read_csv('file1', index_col=0)
df1.combine_first(df2).reset_index()
    Header1 Header2 Header3     Header4
0   aaaaaaa cat dog     ddddddd
1   eeeeeee fffffff ggggggg     hhhhhhh
2   iiiiiii doctor  engineer    lllllll
3   mmmmmmm sky blue        ppppppp

pandas.DataFrame.combine_firstは非常に効率的で,最も単純な結合だと思う.

これを考える.

Awk

1列目をキーにして1つ目のファイルを連想配列に入れる.

%%bash
time {
awk 'BEGIN{FS=OFS=","} NR==FNR{a[$1]=$0;next} {print a[$1](a[$1] ? OFS $4 : $0)}' file2 file1
}
Header1,Header2,Header3,Header4
aaaaaaa,cat,dog,ddddddd
eeeeeee,fffffff,ggggggg,hhhhhhh
iiiiiii,doctor,engineer,lllllll
mmmmmmm,sky,blue,ppppppp

real    0m0.003s
user    0m0.002s
sys 0m0.001s

簡単なお題なので,ちょっとbash(shell script)の練習.

まず,失敗.行が一致していないので,これじゃ駄目.

%%bash
awk 1 file1 >temp1
awk 1 file2 >temp2
time {
while IFS=, read -r f11 f12 f13 f14 <&3 && IFS=, read -r f21 f22 f23 <&4; do
    printf '%s,%s,%s,%s\n' "$f11" "$f22" "$f23" "$f14"
done 3<temp1 4<temp2
}
Header1,Header2,Header3,Header4
aaaaaaa,cat,dog,ddddddd
eeeeeee,doctor,engineer,hhhhhhh
iiiiiii,sky,blue,lllllll

real    0m0.001s
user    0m0.000s
sys 0m0.001s

綺麗汚いはさておき,とりあえずif…else…で誤魔化せばいけるかなと.

%%bash
time {
f=1
while IFS=,; do
    read -r f11 f12 f13 f14 <&3 || break
    if [ $f == 1 ]; then
        read -r f21 f22 f23 <&4 || break
    else
        :
    fi
    if [ "$f11" == "$f21" ]; then
        f=1
        echo "$f11,$f22,$f23,$f14"
    else
        f=0
        echo "$f11,$f12,$f13,$f14"
    fi
done 3<temp1 4<temp2
}
Header1,Header2,Header3,Header4
aaaaaaa,cat,dog,ddddddd
eeeeeee,fffffff,ggggggg,hhhhhhh
iiiiiii,doctor,engineer,lllllll
mmmmmmm,sky,blue,ppppppp

real    0m0.000s
user    0m0.000s
sys 0m0.000s

なんかピンとこない.

 
 

%%writefile file1
Header1,Header2,Header3,Header4
aaaaaaa,bbbbbbb,ccccccc,ddddddd
eeeeeee,fffffff,ggggggg,hhhhhhh
iiiiiii,jjjjjjj,kkkkkkk,lllllll
mmmmmmm,nnnnnnn,ooooooo,ppppppp
%%writefile file2
"Header1","Header2","Header3"
"aaaaaaa","cat","dog"
"iiiiiii","doctor","engineer"
"mmmmmmm","sky","blue"
%%bash
time {
awk 'BEGIN{FS=OFS=","} NR==FNR{a[$1]=$2 OFS $3;next} FNR>1{print a[$1](a[$1] ? OFS $3 OFS $4 : $0)}' <(tr -d '"' <file2) file1
}
cat,dog,ccccccc,ddddddd
eeeeeee,fffffff,ggggggg,hhhhhhh
doctor,engineer,kkkkkkk,lllllll
sky,blue,ooooooo,ppppppp

real    0m0.006s
user    0m0.006s
sys 0m0.004s
%%bash
time {
awk 1 file1 >temp1
tr -d '"' temp2
f=1
cnt=0
while IFS=,; do
    read -r f11 f12 f13 f14 <&3 || break
    (( cnt += 1 ))
    if [ $f == 1 ]; then
        read -r f21 f22 f23 <&4 || break
    else
        :
    fi
    if [ "$cnt" -gt 1 ]; then
        if [ "$f11" == "$f21" ]; then
            f=1
            echo "$f22,$f23,$f13,$f14"
        else
            f=0
            echo "$f11,$f12,$f13,$f14"
        fi
    else
        :
    fi
done 3<temp1 4<temp2
}
cat,dog,ccccccc,ddddddd
eeeeeee,fffffff,ggggggg,hhhhhhh
doctor,engineer,kkkkkkk,lllllll
sky,blue,ooooooo,ppppppp

real    0m0.009s
user    0m0.006s
sys 0m0.005s
カテゴリー: 未分類 | コメントをどうぞ

内部結合(inner join) – Awk/Bash

Merging two csv files, can’t get rid of newline – StackOverflow

%%writefile file1
col2, col6, col7, col17
a, b, c, 145
e, f, g, 101
x, y, z, 243
%%writefile file2
col2, col6, col7, col17
a, b, c, 88
e, f, g, 96
x, k, l, 222
import pandas as pd


df1 = pd.read_csv('file1', sep=',\s+', engine='python')
df2 = pd.read_csv('file2', sep=',\s+', engine='python')
df1.merge(df2, on=['col2', 'col6', 'col7'])
    col2    col6    col7    col17_x col17_y
0   a   b   c   145 88
1   e   f   g   101 96

という処理がしたいという話.要は内部結合(inner join).

Awk

単なる文字列処理として考えるので,内部結合だとか外部結合だとか考える必要はない.指定したいキーに基づいて,配列に代入するだけなので,簡便だし大体の場合に遅くはない(小規模なら確実に速い).

%%bash
time {
awk '
BEGIN{FS=OFS=","}
FNR==1{h=h(i++ ? OFS $4 : $0);if(i==2)print h;next}
NR==FNR{a[$1,$2,$3]=$0;next}
(($1,$2,$3) in a){print a[$1,$2,$3] OFS $4}
' file1 file2
}
col2, col6, col7, col17, col17
a, b, c, 145, 88
e, f, g, 101, 96

real    0m0.003s
user    0m0.001s
sys 0m0.002s
%%bash
time {
for i in {0..100}; do
    awk '
        BEGIN{FS=OFS=","}
        FNR==1{h=h(i++ ? OFS $4 : $0);if(i==2)print h;next}
        NR==FNR{a[$1,$2,$3]=$0;next}
        (($1,$2,$3) in a){print a[$1,$2,$3] OFS $4}
        ' file1 file2 > /dev/null
done
}
real    0m0.273s
user    0m0.121s
sys 0m0.167s

join

joinコマンドはキーがソート済みでかつキー列が1列という強い仮定が必要になるので,今のようにキー列が複数ある場合,キー列の数だけjoinを連結する必要があるので,なんだこれ状態になる.いや,マジで,なんだこれ.

%%bash
time {
join -11 -21 -o "1.1 1.2 1.3 1.4 2.4" file1 file2 | 
join -12 -22 -o "1.1 1.2 1.3 1.4 2.4" - file2 | 
join -13 -23 -o "1.1 1.2 1.3 1.4 2.4" - file2
}
col2, col6, col7, col17 col17
a, b, c, 145 88
e, f, g, 101 96

real    0m0.007s
user    0m0.006s
sys 0m0.005s
%%bash
time {
for i in {0..100}; do
    join -11 -21 -o "1.1 1.2 1.3 1.4 2.4" file1 file2 | 
    join -12 -22 -o "1.1 1.2 1.3 1.4 2.4" - file2 | 
    join -13 -23 -o "1.1 1.2 1.3 1.4 2.4" - file2 > /dev/null
done
}
real    0m0.607s
user    0m0.462s
sys 0m0.555s
%%bash
time {
awk '
BEGIN{FS=OFS=", "}
{k=$1 OFS $2 OFS $3;print k}
!(k in a){a[k]=i++}
NR==FNR{print a[k] OFS $0 >"temp1";next}
{print a[k] OFS $0 >"temp2"}' file1 file2 > /dev/null
join -11 -21 -o "1.2 1.3 1.4 1.5 2.5" temp1 temp2
}
col2, col6, col7, col17 col17
a, b, c, 145 88
e, f, g, 101 96

real    0m0.006s
user    0m0.002s
sys 0m0.005s
%%bash
time {
for i in {0..100}; do
    awk '
    BEGIN{FS=OFS=", "}
    {k=$1 OFS $2 OFS $3;print k}
    !(k in a){a[k]=i++}
    NR==FNR{print a[k] OFS $0 >"temp1";next}
    {print a[k] OFS $0 >"temp2"}' file1 file2 > /dev/null
    join -11 -21 -o "1.2 1.3 1.4 1.5 2.5" temp1 temp2 > /dev/null
done
}
real    0m0.545s
user    0m0.232s
sys 0m0.339s
カテゴリー: 未分類 | コメントをどうぞ

あるファイルの列を別のファイルに直接追加する方法 – Awk/bash

How to directly append columns of one file to another file – StackOverflow

「Requirement: Only grep/cut/join/cat/regex/for/while」って書いてあるので,Awk/Bashの練習がてら,Pythonを使いたいのをグッと堪えていろいろ考えてみる.

%%writefile file1
1
2
3
%%writefile file2
1
2
3

big_file{1,2}として上のファイルをそれぞれx1,000,000したファイルを作成.

比較用にPandas(Python)で処理した場合(Pandasの基本型はデータフレームなので,空間効率はともかく時間効率の観点からはシリーズ型を用いるベネフィットはあまりなく,データフレームが良い).

import pandas as pd


%timeit pd.concat((pd.read_csv(f'file{i}', header=None) for i in range(1, 3)), 1)
%timeit pd.concat((pd.read_csv(f'file{i}', header=None, squeeze=True) for i in range(1, 3)), 1)
%timeit pd.concat((pd.read_csv(f'big_file{i}', header=None) for i in range(1, 3)), 1)
%timeit pd.concat((pd.read_csv(f'big_file{i}', header=None, squeeze=True) for i in range(1, 3)), 1)
1000 loops, best of 3: 1.81 ms per loop
100 loops, best of 3: 2.61 ms per loop
1 loop, best of 3: 381 ms per loop
1 loop, best of 3: 402 ms per loop

Awk

各ファイル1列で行が一致しているので,非常に簡単.

%%bash
time {
awk 'NR==FNR{a[NR]=$1;next}{print a[FNR],$1}' file{1,2}
}
1 1
2 2
3 3

real    0m0.003s
user    0m0.002s
sys 0m0.001s

一旦,配列に入れて処理するので効率は悪い.

%%bash
time {
awk 'NR==FNR{a[NR]=$1;next}{print a[FNR],$1}' big_file{1,2} > /dev/null
}
real    0m2.048s
user    0m1.882s
sys 0m0.165s

Pasteコマンド

column-wise(列方向,行単位)の単純な連結の場合,Pasteが使える.ただ,あれこれ使い分けるのが面倒なので,基本的に存在を忘れる(とりあえずAwkで,的な).それに,(多分)メモリに展開して処理するので,大規模ファイルではメモリ不足を起こしてエラーを出す.

%%bash
time {
paste -d" " file1 file2
}
1 1
2 2
3 3

real    0m0.004s
user    0m0.001s
sys 0m0.003s

やっぱり,この手の処理にはリーズナブルチョイスか.

%%bash
time {
paste -d" " big_file{1,2} > /dev/null 
}
real    0m0.113s
user    0m0.108s
sys 0m0.005s

joinコマンド

pasteでできることはjoinでもできる(pasteでできる場合はpasteの方が速い).joinを使いこなせれば便利だけど,joinコマンドは入力がソート済みのデータでかつキー列が1列のみとか強い仮定が必要になるので,割と不便.linuxのsortは速いし,out-of-coreに処理できるし,並列化もできるので,キー列(キーに指定したい複数列に基づいた1列のキー)でソートしてpasteする方が効率的なんじゃないかな.

%%bash
time {
join -o '1.1 2.1' file{1,2}
}
1 1
2 2
3 3

real    0m0.004s
user    0m0.001s
sys 0m0.003s
%%bash
time {
join -o '1.1 2.1' big_file{1,2} > /dev/null
}

real    0m0.581s
user    0m0.574s
sys 0m0.007s

Bash

回答まま.Colaboratory上(おそらくJupyter Notebookでも同じ)では最後の1行がうまく処理されないが(最後に空行を追加しても変わらず),Linuxのターミナル上ではきちんと動作するのでコードに問題がある訳では無い.なんでだろう.


「%%writefile」でファイル書き込みを行った場合,ファイル終端の空行は削除されて,

%%writefile file1
1
2
3

%%writefile file1_1
1
2
3

with open('file1', 'r') as f:
    s = f.read()
print(repr(s))
with open('file1_1', 'r') as f:
    s = f.read()
print(repr(s))
'1\n2\n3'
'1\n2\n3'

となるのが原因の様だ(今度はなんでそうなるのか気になる所).

%%bash
awk 1 file1 > temp1
with open('temp1', 'r') as f:
    s = f.read()
print(repr(s))
'1\n2\n3\n'

%%bash
time {
while true; do
  read -r f1 <&3 || break
  read -r f2 <&4 || break
  echo "$f1 $f2"
done 3<file1 4<file2
}
1 1
2 2

real    0m0.000s
user    0m0.000s
sys 0m0.000s

めっちゃ速い!?……と思ったら,とんでもなく遅かった.

%%bash
time {
while true; do
  read -r f1 &lt;&amp;3 || break
  read -r f2 &lt;&amp;4 || break
  echo &quot;$f1 $f2&quot;
done 3&lt;big_file1 4<big> /dev/null
}
real    1m21.334s
user    0m42.831s
sys 0m38.491s
%%bash
time {
while read -r f1 &lt;&amp;3 &amp;&amp; read -r f2 &lt;&amp;4; do
    printf &#039;%s %s\n&#039; &quot;$f1&quot; &quot;$f2&quot;
done 3&lt;big_file1 4<big> /dev/null
}
real    1m21.824s
user    0m43.135s
sys 0m38.673s

 
 
関連:
外部結合(outer join)

複数ファイルを読み込んでソート

カテゴリー: 未分類 | コメントをどうぞ

scipy.optimize.minimizeを用いて期待される出力を得る

How to avoid NaN in a weighted average? – StackOverflow

質問の意図とか,内容をガン無視して,最適化問題として考える.

 

追記:

「How to avoid NaN in a weighted average」を満たすリーズナブルチョイスは,「numpy.ma.average」を用いる.

arr = df.values
a = np.ma.array(arr, mask=np.isnan(arr))
np.ma.average(a, 0, weights=[0.2, 0.3, 0.5])
masked_array(data=[3.0000000000000004, 3.4, 3.2, 3.2],
             mask=[False, False, False, False],
       fill_value=1e+20)

或いは,Pandasで処理したいと思うと,weighted average(重み付け平均)のメソッドが(多分)無いので,以下の様な形で自分で計算する必要がある.

w = np.array([0.2, 0.3, 0.5])
v = df.fillna(0).T
v.dot(w).div(v.ne(0).dot(w))
str1    3.0
str2    3.4
str3    3.2
str4    3.2
dtype: float64

 

閑話休題,

以下で示されるようなデータフレームがある.

       str1    str2     str3     str4    
key1     3       4       2        5
key2    NaN      3       4        4
key3    NaN     NaN     NaN       2

以下のウェイトベクトルを掛けて,

[0.2, 0.3, 0.5]

期待される出力として以下を得たい.

          str1   str2  str3    str4
    0      3     3.45   3.1     4.7

 
 
趣旨の様に,単にNaNを0に落とすと,期待される出力は(少なくとも線形モデルでは)得られない.

import io
import pandas as pd
import numpy as np


strings = """       str1    str2     str3     str4    
key1     3       4       2        5
key2    NaN      3       4        4
key3    NaN     NaN     NaN       2"""
df = pd.read_csv(io.StringIO(strings), sep='\s+')
print(df)

arr = df.fillna(0).values.T
print(arr.dot([0.2, 0.3, 0.5])) # expected: [3, 3.45, 3.1, 4.7]...?

expected = np.array([3, 3.45, 3.1, 4.7])
sol = np.linalg.lstsq(arr, expected, rcond=-1) # np.linalg.solve(arr, expected)
print(arr.dot(sol[0]), sol[1])
      str1  str2  str3  str4
key1   3.0   4.0   2.0     5
key2   NaN   3.0   4.0     4
key3   NaN   NaN   NaN     2
[0.6 1.7 1.6 3.2]
[2.49230769 4.05923077 2.64307692 4.7       ] [0.83769231]

NaNが不定として考えて,期待される出力が得られないか考えたい.こういう感じで.

import numpy as np
from scipy.optimize import minimize


def f(x, s=None, t=None, u=None, v=None):
    if s is None:  s = x[3]
    if t is None:  t = x[4]
    if u is None:  u = x[5]
    if v is None:  v = x[6]
    return x[0]*s + x[1]*t + x[2]*u - v


cons = ({'type': 'eq', 'fun' : lambda x: f(x, 3, None, None, 3)},
    {'type': 'eq', 'fun' : lambda x: f(x, 4, 3, None, 3.45)},
    {'type': 'eq', 'fun' : lambda x: f(x, 2, 4, None, 3.1)},
    {'type': 'eq', 'fun' : lambda x: f(x, 5, 4, 2, 4.7)})
 

res = minimize(f, x0=(0.831, 0.245, -0.218, 0, 0, 0, 0), constraints=cons, options={'disp': True})
print(res)
Optimization terminated successfully.    (Exit mode 0)
            Current function value: -242749624.0564763
            Iterations: 28
            Function evaluations: 272
            Gradient evaluations: 28
     fun: -242749624.0564763
     jac: array([-1.27394946e+08,  4.00000000e+00,  1.35074000e+05,  0.00000000e+00,
        0.00000000e+00,  0.00000000e+00,  0.00000000e+00])
 message: 'Optimization terminated successfully.'
    nfev: 272
     nit: 28
    njev: 28
  status: 0
 success: True
       x: array([ 4.69230988e-01,  5.88461977e-01, -1.42373618e-06, -1.27394946e+08,
        3.03268009e+00,  1.35074100e+05,  1.82971969e+08])
w = np.array([4.69230988e-01,  5.88461977e-01, -1.42373618e-06])
arr[0, 1] = 3.03268009e+00
arr[:-1, 2] = 1.35074100e+05
arr.dot(w)
array([3.  , 3.45, 3.1 , 4.7 ])

或いは,

import numpy as np
from scipy.optimize import minimize


def f(x, s, t, u):
    if t is None:  t = x[3]
    if u is None:  u = x[4]
    return x[0]*s + x[1]*t + x[2]*u


cons = ({'type': 'eq', 'fun' : lambda x: f(x, 3, None, None)-3},
    {'type': 'eq', 'fun' : lambda x: f(x, 4, 3, None)-3.45},
    {'type': 'eq', 'fun' : lambda x: f(x, 2, 4, None)-3.1},
    {'type': 'eq', 'fun' : lambda x: f(x, 5, 4, 2)-4.7}) 

res = minimize(f, x0=(0.2, 0.3, 0.5, 0, 0), args=(0, 0, 0), constraints=cons, options={'disp': True})
print(res)
Optimization terminated successfully.    (Exit mode 0)
            Current function value: 0.0
            Iterations: 4
            Function evaluations: 29
            Gradient evaluations: 4
     fun: 0.0
     jac: array([0., 0., 0., 0., 0.])
 message: 'Optimization terminated successfully.'
    nfev: 29
     nit: 4
    njev: 4
  status: 0
 success: True
       x: array([0.3332825 , 0.31656499, 0.88366377, 2.63130005, 1.32083627])
w = res.x[:3]
arr[0, 1] = res.x[3]
arr[:-1, 2] = res.x[4]
arr.dot(w)
array([3.00000013, 3.45000013, 3.10000013, 4.7       ])

これは,ウェイトまで変えてしまっているけど.各行でNaNに自由に値を入れていい,とすると,単に任意の有理数を入れれば良いという話になってしまうので,各行のNaNは同じと仮定すれば,

import numpy as np
from scipy.optimize import minimize


def f(x, s, t, u):
    if t is None:  t = x[0]
    if u is None:  u = x[1]
    return 0.2*s + 0.3*t + 0.5*u


cons = ({'type': 'eq', 'fun' : lambda x: f(x, 3, None, None)-3},
    {'type': 'eq', 'fun' : lambda x: f(x, 4, 3, None)-3.45},
    {'type': 'eq', 'fun' : lambda x: f(x, 2, 4, None)-3.1})

res = minimize(f, x0=(3, 3), args=(0, 0, 0), constraints=cons, options={'disp': True})
print(res)
More equality constraints than independent variables    (Exit mode 2)
            Current function value: 2.4
            Iterations: 1
            Function evaluations: 4
            Gradient evaluations: 1
     fun: 2.4
     jac: array([0.30000001, 0.5       ])
 message: 'More equality constraints than independent variables'
    nfev: 4
     nit: 1
    njev: 1
  status: 2
 success: False
       x: array([3., 3.])

独立変数よりも等式制約の方が多いと怒られた.よくみたら,「0.5u=1.75」と「0.5u=1.5」が成立し得ないし,与えられたWeightではstr4を満たさないので,切片1.5としたり,少しイジって考えると,

import io
import pandas as pd
import numpy as np


strings = """       str1    str2     str3     str4    
key1     3       4       2        5
key2    NaN      3       4        4
key3    NaN     NaN     NaN       2"""
df = pd.read_csv(io.StringIO(strings), sep='\s+')
print(df)

arr = df.fillna(0).values.T
print(arr.dot([0.2, 0.3, 0.5])) # expected: [3, 3.45, 3.1, 4.7]...?

a = np.array(
    [
        [0.3, 0.5],
        [0, 0.5],
        [0, 0.5]
    ]
)
e = np.array([0.9, 0.25, 0])
c = np.linalg.lstsq(a, e, rcond=-1)[0]
arr[0, 1] = c[0]
arr[:-1, 2] = c[1]
print(arr.dot([0.2, 0.3, 0.5]) + 1.5)
      str1  str2  str3  str4
key1   3.0   4.0   2.0     5
key2   NaN   3.0   4.0     4
key3   NaN   NaN   NaN     2
[0.6 1.7 1.6 3.2]
[3.    3.325 3.225 4.7  ]

 
 
関連:
scipy.optimize.minimizeで積分

scipy.optimize.minimize

局所的最適解/大域的最適解(ローカルミニマム/グローバルミニマム)

最大尤度モデルの推定

x3 – 2x2 – 3x – y**2 – (x + y – 1) = 0

カテゴリー: 未分類 | コメントをどうぞ

Numpyでrolling windowを適用する様な処理

How to find where in numpy array a zero element is preceded by at least N-1 consecutive zeros? – StackOverflow

過去にも何度か触れているが,この手の処理の場合はコンボリューションを考えるのがリーズナブルチョイス.ただ,考え方としては,Numpyでrolling windowを適用するような処理(pandas.Series.rollingの様な処理),窓を掛けて移動させながら処理させる様なイメージのものは大体2つの方法で処理できる.一つは「n個ずつズラして任意の処理をする~コンボリューション」という考え方,もう一つは「numpy.lib.stride_tricks.as_strided」を用いて任意形状のビューを作って処理するという考え方.それと,2つ目の派生というか,応用になるけど(2つ目の方が汎用性が高い),新しい軸に沿って任意形状が欲しい場合,numpy.ndarray.takeを用いる方法もある.

import numpy as np


def nzeros(a, k=3):
    dtype = np.uint8
    p = np.ones(k, dtype=dtype)
    m = (a==0).astype(dtype)
    s = np.convolve(m, p)
    return (s==k).astype(dtype)[:-k+1]


def slided_view(arr, N=3, S=1):
    s = arr.strides[0]
    nrows = (arr.size-N) // S + 1
    strided = np.lib.stride_tricks.as_strided
    return strided(arr, shape=(nrows, N), strides=(S*s, s))


def nzeros_stride_tricks(a, k=3):
    dtype = np.uint8
    p = np.zeros(k, dtype=dtype)
    return np.concatenate((p[:-1], (slided_view(a, k)==p).all(1).astype(dtype)))


def nzeros_take(a, k=3):
    dtype = np.uint8
    p = np.zeros(k, dtype=dtype)
    ind = np.arange(len(a)-k, dtype=dtype)[:, None] + np.arange(k, dtype=dtype)
    return np.concatenate((p[:-1], (a.take(ind)==p).all(1).astype(dtype)))


a = np.array([0, 0, 0, 0, 1, 0, 0, 0, 1, 1])
print(nzeros(a))
%timeit nzeros(a)
print(nzeros(a, 2))
%timeit nzeros(a, 2)

print(nzeros_stride_tricks(a))
%timeit nzeros_stride_tricks(a)
print(nzeros_stride_tricks(a, 2))
%timeit nzeros_stride_tricks(a, 2)

print(nzeros_take(a))
%timeit nzeros_take(a)
print(nzeros_take(a, 2))
%timeit nzeros_take(a, 2)
[0 0 1 1 0 0 0 1 0 0]
100000 loops, best of 3: 9.02 µs per loop
[0 1 1 1 0 0 1 1 0 0]
100000 loops, best of 3: 8.93 µs per loop
[0 0 1 1 0 0 0 1 0 0]
100000 loops, best of 3: 17.1 µs per loop
[0 1 1 1 0 0 1 1 0 0]
100000 loops, best of 3: 17 µs per loop
[0 0 1 1 0 0 0 1 0]
100000 loops, best of 3: 12.8 µs per loop
[0 1 1 1 0 0 1 1 0]
100000 loops, best of 3: 12.6 µs per loop

 
 
関連:
3つの連続する整数の合計値が最も大きいインデックスを探す

Pythonのリストで3つの最も類似した値をみつける

Pandas Time Series dataから周期性をみつける

numpy.lib.stride_tricks.as_strided

ある配列に含まれる任意の配列をみつける

任意の配列(シリーズ)からタイムラグのあるパンダデータフレームを作成

与えられたランクを表す配列に基づいて固定長のすべての部分配列を探す

numpy.ndarrayをn回シフトして行列を作成

pandas.DataFrameから前3行の値のリストを作成

新しい軸に沿って任意のアレイに基づいてシフトした配列の作成

カテゴリー: 未分類 | コメントをどうぞ