たつをの ChangeLog

36 件 見つかりました。

1 2 3 4 5 6 7 8 [ 次へ ]

CSV ファイルで、テキストのカラムにカンマが入っているんだけど、そのカラムがダブルクォート文字で囲まれていないときの対処法について。

ときどきそういうデータがあるのです。
適当なその場しのぎスクリプトで CSV 出力した結果とか。
で、それをなんとかして使わないといけない場面もあるのです。
怒ってないです。

例えば、全部で10カラムのCSV。
第4カラムにテキスト。

0,2,0,こんにちは,50,0,0,0,0,0
1,1,1,さようなら,0,0,0,0,0,0

CSV だからカンマ区切りなんだけど、テキストにカンマが含まれてる場合あり。
しかもダブルクォートで囲まれていない。
区切りのカンマと区別つかなくて困る。

理想:
1,1,0,"ああ,いい,ううう。",21,0,0,0,0,0
0,2,0,"あれれれ,",50,0,0,0,0,0

現実:
1,1,0,ああ,いい,ううう。,21,0,0,0,0,0
0,2,0,あれれれ,,50,0,0,0,0,0

こういうときは、前から3カラム(=4-1)、後ろから6カラム(=10-4)を除いた残りすべてを一つのテキストとする作戦で。

1,1,0,ああ,いい,ううう。,21,0,0,0,0,0

↓ カンマで分割してリストにする

1 1 0 ああ いい ううう。 21 0 0 0 0 0

↓ 前と後ろのカラムを取る

[1 1 0] ああ いい ううう。 [21 0 0 0 0 0]

↓

ああ いい ううう。

↓ 残りをカンマで繋ぎ直し、ダブルクォートで囲む

"ああ,いい,ううう。"

↓ さっき取った、前と後ろのカラムと合わせて最終的な CSV にする

1,1,0,"ああ,いい,ううう。",21,0,0,0,0,0

Perl


Perl で書くとこんな感じ。
my $IX = 4; # 4番目のカラムがカンマ入りテキスト
my $N = 10; # 全部で10カラム
while (<>) {
    chomp; # 末尾の改行削除
    my @F = split(",", $_, -1); # 読み込んだ CSV 行をカンマで切ってリストへ
    my @pre = @F[0..($IX-2)]; # 前から IX-1 個分のカラム
    my @post = @F[$#F-($N-$IX-1)..$#F]; # 後ろから N-IX 個分のカラム
    my $text = join(",", @F[($IX-1)..($#F-($N-$IX))]); # カンマ入りテキスト
    print join(",", @pre, "\"$text\"", @post)."\n"; # CSV に戻す
}

splice 関数を使ったバージョン。
my $IX = 4; # 4番目のカラムがカンマ入りテキスト
my $N = 4; # 全部で10カラム
while (<>) {
    chomp; # 末尾の改行削除
    my @F = split(",", $_, -1); # 読み込んだ CSV 行をカンマで切ってリストへ
    my @ts = join(",", splice(@F, $IX-1, (@F-$N) + 1)); # テキスト部分の抜き出し
    splice(@F, $IX-1, 0, qq(").join(",", @ts).qq(")); # ""をつけて戻す
    print join(",", @F)."\n"; # CSV に戻す
}

Perl の split 関数の第3引数の "-1" についてはこちらを参照。

Python


Python で書くとこんな感じ。
右側から分割してくれる rsplit 関数がこの用途に便利。
ID = 4
N = 10
with open('sample.csv') as f:
    for line in f.read().splitlines():
        pre = line.split(",", ID - 1)
        post = pre[-1].rsplit(",", N - ID)
        l = pre[:-1]
        l.append('"' + post[0] + '"')
        l.extend(post[1:])
        print(",".join(l))

1,1,0,ああ,いい,ううう。,21,0,0,0,0,0

↓ 最初の split で前から4分割(3箇所で切断)

1 / 1 / 0 / ああ,いい,ううう。,21,0,0,0,0,0

↓ 一番右の塊を次の rsplit で後ろから7分割(6箇所で切断)。

ああ,いい,ううう。 / 21 / 0 / 0 / 0 / 0 / 0

↓

1 / 1 / 0 / ああ,いい,ううう。 / 21 / 0 / 0 / 0 / 0 / 0

↓

1,1,0,"ああ,いい,ううう。",21,0,0,0,0,0

おわりに


カンマ入りテキストカラムが複数ある場合はちょっと難しい。めんどくさいのであきらめましょう。桁区切りカンマ入りの数値がクォート無しで複数カラムあるとかも。

あと、上のスクリプトではテキストにダブルクォートが含まれててエスケープされてない場合は考慮してません。join で "\t" を使って TSV にしとくのが良いかと。テキストに "\t" がない前提で。

形態素解析器 MeCab の WebAPI である MECAPI
その MECAPI を Linux や macOS などのターミナルから使うためのコマンドを用意しました。
Perl で書いた超簡単なやつです。


使い方は API に準拠 (ref. http://maapi.net/apis/mecapi)。
response, filter, format, dic はオプションで指定。
sentense (解析対象文) は標準入力で。

実行例:
% echo 'ここは六本木ヒルズです' | mecapi.pl -format json -dic neologd | jq .
[
  {
    "feature": "名詞,代名詞,一般,*,*,*,ここ,ココ,ココ",
    "surface": "ここ"
  },
  {
    "feature": "助詞,係助詞,*,*,*,*,は,ハ,ワ",
    "surface": "は"
  },
  {
    "feature": "名詞,固有名詞,一般,*,*,*,六本木ヒルズ,ロッポンギヒルズ,ロッポンギヒルズ",
    "surface": "六本木ヒルズ"
  },
  {
    "feature": "助動詞,*,*,*,特殊・デス,基本形,です,デス,デス",
    "surface": "です"
  }
]
(JSON を扱うには jq がすごく便利!)

エラーが出る場合、中で使っている Perl モジュール "LWP::Simple" で https へのアクセスができないことが原因かも。
そのときは、 "LWP::Protocol::https" を入れて見てください。
"perl lwp::simple https" などでネット検索!

それができない、とか、それでもダメといった場合は代わりに mecapi-curl.pl をご利用ください。
こちらは最小構成の perl と curl が入っていれば動きます。
単なる curl のラッパーです。

以上です。
ぜひ、MECAPI をお楽しみください!

追記191212: 一文ずつ渡すだけならば普通に curl だけOKです!
curl https://maapi.net/apis/mecapi -s --data format=json --data-urlencode sentence=六本木ヒルズです

関連記事


MECAPI という形態素解析 API を運営しているのですが、ここしばらくはレンタルサーバの負荷がきついので実質まったく動いていない状態でした(というか停止していた)。

「これじゃいけない!」ということで、AWS 上で MECAPI を運用していくことにしました。
クラウドな MECAPI です。

URLも新しくなりました。
説明ページ(トップページ)も作りました。
どうぞご利用ください。

mecapi 仮ロゴ
  • 形態素解析API「MECAPI」
    MECAPIとは、 日本語形態素解析器 MeCab の Web Service (API) です。
    API を無償で提供しております。
    後述の注意点をお読みいただいた上で、ご自由にお使いください。

1週間くらい前から真面目にAWSをいじり始めたのですが、かなり良いですね(今更)。
練習でウェブサーバを立ち上げたりしてましたが、手順は多いけど難しくはないです。
なんだかんだで最後までできてしまいます。
やはり時代はクラウドだなあ。

MECAPI での形態素解析処理(MeCab とその Perl Module)は当面はインスタンス1つだけでまかなうつもり。
ロードバランサーの設定もしたので、負荷がきつくなったらインスタンスを追加すれば大丈夫なはず(今はLBにインスタンスが一つだけぶらさがっている状態)。
そこらへんは追々に。

AWS の料金ですが、私が運営しているKindleセール情報サイト「キンセリ」の収入の一部を使っていくつもりです。
まずは、月10万円くらい($1,000)が上限(アラート設定済み)。
どれくらいかかるかはやってみなくちゃわからないけど。
運用状況については今後定期的に報告していく所存です。

過去記事

この記事に言及しているこのブログ内の記事

今さらなメモ。
"%uXXXX" 形式の文字列を Perl でデコードする方法。

s/%u([A-Fa-f0-9]{4})/chr(hex($1))/eg

perl -le '$a = "%u3044%u3061%u3054";
$a =~ s/%u([A-Fa-f0-9]{4})/chr(hex($1))/eg;
print $a'
→
いちご

URI::Escape::JavaScript の unescape でもいけるみたい。

作業メモ的な話。
ヒートマップに使いたいので、ターミナルエミュレータで出せる色について調べて、簡単なプログラム書いて確認した。
Macのターミナルでも、Windowsのteratermでも出るやつ。

terminal%20color%20sample

ソース (perl)
#!/usr/bin/env perl
# -*- coding: utf-8 -*-
use strict;
use warnings;

sub rgb2code {
    my ($r, $g, $b) = @_;
    my $v = $r * 36 + $g * 6 + $b + 16;
    return $v;
}

##########
print "基本色:暗め、明るめ\n";
##########

for (my $i = 0; $i <= 7; $i++) {
    my $v = $i;
    printf("\033[48;5;%dm %02X,%3d \033[0m", $v, $v, $v);
}
print "\n";
for (my $i = 8; $i <= 15; $i++) {
    my $v = $i;
    printf("\033[48;5;%dm %02X,%3d \033[0m", $v, $v, $v);
}
print "\n";
print "\n";

##########
print "グレースケール\n";
##########

for (my $i = 232; $i <= 255; $i++) {
    my $v = $i;
    printf("\033[48;5;%dm %02X \033[0m", $v, $v);
}
print "\n";
print "\n";

##########
print "数値順 (Hex,Dec,RGB)\n";
##########

for (my $r = 0; $r <= 5; $r++) {
    for (my $g = 0; $g <= 5; $g++) {
        for (my $b = 0; $b <= 5; $b++) {
            my $v = rgb2code($r, $g, $b);
            printf("\033[48;5;%dm %02X,%3d,%s \033[0m", $v, $v, $v, $r.$g.$b);
        }
        print "\n";
    }
}
print "\n";

##########
print "色相環の配列:赤から紫へ\n";
##########

my ($r, $g, $b) = (5, 0, 0);
for ($g = 0; $g <= 5; $g++) {
    my $v = rgb2code($r, $g, $b);
    printf("\033[48;5;%dm %02X,%3d,%s \033[0m\n", $v, $v, $v, $r.$g.$b);
}
$g = 5;
for ($r = 4; $r >= 0; $r--) {
    my $v = rgb2code($r, $g, $b);
    printf("\033[48;5;%dm %02X,%3d,%s \033[0m\n", $v, $v, $v, $r.$g.$b);
}
$r = 0;
for ($b = 1; $b <= 5; $b++) {
    my $v = rgb2code($r, $g, $b);
    printf("\033[48;5;%dm %02X,%3d,%s \033[0m\n", $v, $v, $v, $r.$g.$b);
}
$b = 5;
for ($g = 4; $g >= 0; $g--) {
    my $v = rgb2code($r, $g, $b);
    printf("\033[48;5;%dm %02X,%3d,%s \033[0m\n", $v, $v, $v, $r.$g.$b);
}
print "\n";

##########
print "プラス値のみのヒートマップ用の配色案\n";
##########

($r, $g, $b) = (5, 0, 0);
for ($g = 0; $g <= 5; $g++) {
    my $v = rgb2code($r, $g, $b);
    printf("\033[48;5;%dm %02X,%3d,%s \033[0m\n", $v, $v, $v, $r.$g.$b);
}
$g = 5;
for ($b = 1; $b <= 4; $b++) {
    my $v = rgb2code($r, $g, $b);
    printf("\033[48;5;%dm %02X,%3d,%s \033[0m\n", $v, $v, $v, $r.$g.$b);
}
print "\n";


参考文献


1 2 3 4 5 6 7 8 [ 次へ ]

たつをの ChangeLog
Powered by chalow