English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Pythonでの探索的データ分析(機能的)

ログファイルのエキスを処理するためのいくつかのヒントがあります。企業版Splunkエキスを確認していると仮定します。Splunkでデータを探索できます。または、シンプルなエキスを取得してPythonでデータを扱うことができます。

Pythonでさまざまな実験を実行することは、Splunkでこのような探索的な操作を試みるよりも効果的です。主に、データに対して何でも自由にできるためです。非常に複雑な統計モデルを一箇所で作成できます。

理論的には、Splunkで多くの探索を行うことができます。さまざまな報告と分析機能があります。

しかし...

Splunkを使用するには、私たちが何を探しているかを知っていることを仮定する必要があります。多くの場合、私たちは何を探しているかを知りません:私たちは探索しています。RESTful APIが遅い兆候があるかもしれませんが、それ以上です。どうやって続けますか?

最初のステップはCSV形式の原始データを取得することです。どうすればいいのでしょうか?

原始データの読み取り

まず、CSV.DictReaderオブジェクトを包装するための追加の関数を使用します。

オブジェクト指向の纯粹主義者はこの戦略に反対します。「なぜDictReaderを拡張しないの?」と彼らは尋ねます。私は良い答えを持っていません。私は関数プログラミングとコンポーネントの正交性を好みます。純粋なオブジェクト指向の方法では、これを実現するためにより複雑なハイブリッドを実装する必要があります。

私たちがログを処理する一般的なフレームワークはこのようにです。

with open("somefile.csv") as source:
rdr = csv.DictReader(source)

これにより、CSV形式のSplunkエキスを読み取ることができます。読み取り器内の行をイテレートできます。これはコツ#1これは非常に難しいことではありませんが、私はそれが好きです。

with open("somefile.csv") as source:
rdr = csv.DictReader(source)
for row in rdr:
print("{host} {ResponseTime} {source} {Service}".format_map(row))

私たちは - ある程度に - 有用な形式で元のデータを報告します。出力を装飾したい場合は、フォーマット文字列を変更できます。それは「{ホスト:30s} {返信時間:8s} {ソース:s}”やそれに似たものです。

フィルタリング

一般的には、多くのものを抽出していますが、実際にはサブセットを見る必要があるだけです。Splunkフィルタを変更できますが、探索を完了する前にフィルタの過度使用は不快です。Pythonではフィルタリングが簡単です。必要なものがわかると、Splunkで完了できます。

with open("somefile.csv") as source:
rdr = csv.DictReader(source)
rdr_perf_log = (row for row in rdr if row['source'] == 'perf_log')
for row in rdr_perf_log:
print("{host} {ResponseTime} {Service}".format_map(row))

生成器表現式を追加して、元の行をフィルタリングし、意味のあるサブセットを処理できるようにしました。

投影

在某些情况下,我们会添加额外的源数据列,这些列我们并不想使用。所以将通过对每一行进行投影来消除这些数据。

原则上,Splunk从不产生空列。但是,RESTful API日志可能会导致数据集中包含大量列标题,这些列标题是基于请求URI一部分的代理键。这些列将包含来自使用该代理键的一个请求的一行数据。对于其他行,在这一列中没有任何用处。所以要删除这些空列。

我们也可以用一个生成器表达式来做到这一点,但是它会变得有点长。生成器函数更容易阅读。

def project(reader):
for row in reader:
yield {k:v for k,v in row.items() if v}

我们已经从原始阅读器中的一部分项目构建了一个新的行字典。我们可以使用它来包装我们的过滤器的输出。

with open("somefile.csv") as source:
rdr = csv.DictReader(source)
rdr_perf_log = (row for row in rdr if row['source'] == 'perf_log')
for row in project(rdr_perf_log):
print("{host} {ResponseTime} {Service}".format_map(row))

这将减少在for语句内部可见的未使用的列。

符号更改

row['source']符号会变得比较笨重。使用types.SimpleNamespace比用字典更好。这使得我们可以使用row.source。

这是一个很酷的技巧来创造更有用的东西。

rdr_ns= (types.SimpleNamespace(**row) forrowinreader)

我们可以将其折叠成这样的步骤序列。

with open("somefile.csv") as source:
rdr = csv.DictReader(source)
rdr_perf_log = (row for row in rdr if row['source'] == 'perf_log')
rdr_proj = project(rdr_perf_log)
rdr_ns = (types.SimpleNamespace(**row) for row in rdr_proj)
for row in rdr_ns:
print("{host} {ResponseTime} {Service}".format_map(vars(row)))

请注意我们对format_map()方法的小改动。从SimpleNamespace的属性中,我们添加了vars()函数来提取字典。

我们可以用其他函数把它写成一个函数来保留句法对称性。

def ns_reader(reader):
return (types.SimpleNamespace(**for row in reader)

的确,我们可以把它写成一个像函数一样使用的lambda结构

ns_reader = lambda reader: (types.SimpleNamespace(**for row in reader)

ns_reader()関数とns_reader()lambdaの使用方法は同じですが、lambdaに対するドキュメントストリングとdoctestユニットテストを書くことは少し難しいです。この理由から、lambda構造を使用することを避けるべきです。

map(lambda row:types.SimpleNamespace(** row),reader)。一部の人々はこの生成器式を好みます。

適切なfor文と内部的なyield文を使ってできますが、小さなものから大きなものを書くことは何の利点もありません。

私たちは多くの選択肢があります。なぜなら、Pythonは非常に多くの関数式プログラミング機能を提供しているからです。Pythonを機能性言語として頻繁に見なさない場合でも、シンプルなマッピングを処理するためのさまざまな方法があります。

マッピング:変換と派生データ

私たちは非常に明確なデータ変換リストを持っています。また、派生データプロジェクトが増えていくリストがあります。派生プロジェクトは動的なもので、私たちがテストしている異なる仮説に基づいています。私たちが実験や問題を持つたびに、派生データを変更することがあります。

これらのステップの各々:フィルタリング、プロジェクション、変換、派生はすべてmapです-reduceパイプラインの「map」部分のステージです。小さな関数を作成してmap()に適用することができます。私たちは状態を持つオブジェクトを更新しているため、一般的なmap()関数を使用することができません。より純粋な関数式プログラミングスタイルを実現するために、可変のSimpleNamespaceではなく不可変のnamedtupleを使用します。

def convert(reader):
for row in reader:
row._time = datetime.datetime.strptime(row.Time, "%Y"-%m-%dT%H:%M:%S.%F%Z")
row.response_time = float(row.ResponseTime)
yield row

私たちの探索の過程で、この変換関数の主体を調整します。もしかしたら、最小の変換と派生から始めるかもしれません。私たちは「これが正しい?」という質問を使って探索を続けます。それが動作しない場合、それらから取り除きます。

私たちの全体の処理プロセスは以下の通りです:

with open("somefile.csv") as source:
rdr = csv.DictReader(source)
rdr_perf_log = (row for row in rdr if row['source'] == 'perf_log')
rdr_proj = project(rdr_perf_log)
rdr_ns = (types.SimpleNamespace(**row) for row in rdr_proj)
rdr_converted = convert(rdr_ns)
for row in rdr_converted:
row.start_time = row._time - datetime.timedelta(seconds=row.response_time)
row.service = some_mapping(row.Service)
print( "{host:}30s} {start_time:%H:%M:%S} {response_time:}6.3f} {service}".format_map(vars(row)))

文の主語の変更に注意してください。convert()関数が確定した値を生成します。forループにいくつかの追加の変数を追加しましたが、私たちは100%確定。convert()関数を更新する前に、それらが役立つか(正しいかどうかも)確認します。

減量

減量の面では、少し異なる加工方法を取ることができます。私たちの前の例を再構成し、生成器関数に変換する必要があります。

def converted_log(some_file):
with open(some_file) as source:
rdr = csv.DictReader(source)
rdr_perf_log = (row for row in rdr if row['source'] == 'perf_log')
rdr_proj = project(rdr_perf_log)
rdr_ns = (types.SimpleNamespace(**row) for row in rdr_proj)
rdr_converted = convert(rdr_ns)
for row in rdr_converted:
row.start_time = row._time - datetime.timedelta(seconds=row.response_time)
row.service = some_mapping(row.Service)
yield row

その後、print()の代わりにyieldを使用しました。

これは再構成の一部です。

for row in converted_log("somefile.csv"):
print( "{host:}30s} {start_time:%H:%M:%S} {response_time:}6.3f} {service}".format_map(vars(row)))

理想的には、私たちのすべてのプログラミングがこんな感じです。データを生成するための生成器関数を使用し、データの最終表示を完全に分離します。これにより、自由に再構成および変更を行うことができます。

今や、行をCounter()オブジェクトに収集したり、統計情報を計算するなどのことができます。defaultdict(list)を使用して、サービスごとに行をグループ化できます。

by_service = defaultdict(list)
for row in converted_log("somefile.csv"):
by_service[row.service] = row.response_time
for svc in sorted(by_service):
m = statistics.mean(by_service[svc])
print( "{svc:}15s} {m:.2f}".format_map(vars()) )

我们决定在这里创建具体的列表对象。我们可以使用itertools按服务分组响应时间。它看起来像是正确的函数式编程,但是这种实施在Pythonic函数式编程形式中指出了一些限制。要么我们必须对数据进行排序(创建列表对象),要么在分组数据时创建列表。为了做好几个不同的统计,通过创建具体的列表来分组数据通常更容易。

我们现在正在做两件事情,而不是简单地打印行对象。

创建一些局部变量,如svc和m。我们可以很容易地添加变化或其他措施。

使用没有参数的vars()函数,它会从局部变量中创建一个字典。

这个使用vars()而没有参数的行为就像locals()一样是一个方便的技巧。它允许我们简单地创建我们想要的任何局部变量,并将它们包含在格式化输出中。我们可以侵入我们认为可能相关的各种统计方法中。

既然我们的基本处理循环是针对converted_log(“somefile.csv”)中的行,我们可以通过一个小小的,易于修改的脚本探索很多处理选择。我们可以探索一些假设来确定为什么某些RESTful API处理速度慢,而其他处理速度则很快。

总结

以上所述是小编给大家介绍的Python中的探索性数据分析(功能式),希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对呐喊教程的支持!

声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:notice#oldtoolbag.com(はつしめじで、#を@にかえてください)にはつけをし、かんけいしょうを、もとづけ、そくせいしたち、ほんじょうはいちにせんりょくいんよういんをさくじゅつします。

おすすめ