English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
이에 대한 몇 가지 기술이 있습니다. Enterprise 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()函數。如果我們想實現一個更純粹的函數式編程風格,我們將使用一個不可變的namedtuple而不是一個可變的SimpleNamespace。
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 = 转换(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} {服务}".format_map(vars(row)) )
請注意語句主體的變化。convert()函數產生我們確定的值。我們已在for循環中添加了一些額外的變數,我們不能100%確定。在更新convert()函數之前,我們會看看它們是否有用(甚至是正確的)。
减量
在减量方面,我们可以采取稍微不同的加工方式。我们需要重构我们之前的例子,并把它变成一个生成器函数。
def 转换日志(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 = 转换(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
接着用一个yield代替了print()。
这是重构的另一部分。
for row in 转换日志("somefile.csv"): print( "{host:30s} {start_time:%H:%M:%S} {response_time:6.3f} {服务}".format_map(vars(row)) )
理想情况下,我们所有的编程都是这样的。我们使用生成器函数来生成数据。数据的最终显示保持完全分离。这使我们可以更自由地重构和改变处理。
现在我们可以做一些事情,例如将行收集到Counter()对象中,或者可能计算一些统计信息。我们可以使用defaultdict(list)按服务对行进行分组。
按服务= defaultdict(list) for row in 转换日志("somefile.csv"): 按服务[row.service] = row.response_time for svc in sorted(按服务): m = 统计学.mean(按服务[svc] ) print( "{svc:15s} {m:.2f".format_map(vars()) )"
우리는 여기서 특정한 리스트 객체를 생성하기로 결정했습니다. itertools를 사용하여 서비스별 응답 시간을 그룹화할 수 있습니다. 이는 올바른 함수형 프로그래밍처럼 보이지만, 이 구현은 Pythonic 함수형 프로그래밍 형식에서 몇 가지 제한을 제시합니다. 데이터를 정렬하거나(리스트 객체를 생성하거나) 데이터를 그룹화할 때는 리스트를 생성하여 여러 가지 통계를 수행하는 것이 일반적으로 더 쉽습니다.
지금 우리는 단순히 행 객체를 출력하는 것 대신 두 가지 일을하고 있습니다.
svc와 m와 같은 지역 변수를 생성하면, 변화나 다른 조치를 쉽게 추가할 수 있습니다.
매개변수 없는 vars() 함수를 사용하면, 지역 변수에서 딕셔너리를 생성합니다.
vars()를 매개변수 없이 사용하는 것은 locals()와 같이 편리한 기술입니다. 원하는 어떤 지역 변수든지 간단히 생성하고, 그것을 포맷된 출력에 포함할 수 있습니다. 우리는我们认为可能相关的各种统计方法中.에 침입할 수 있습니다.
우리의 기본 처리 루프가 converted_log(“somefile.csv”)의 행에 대하여 있다면, 작은하고 수정하기 쉬운 스크립트를 통해 많은 처리 선택을 탐색할 수 있습니다. 일부 RESTful API가 처리가 느리다고 느껴지는 이유와 다른 API가 빠르게 처리되는 이유를 결정하기 위한 몇 가지 가정을 탐색할 수 있습니다.
정리
위에서 소개한 Python의 탐색적 데이터 분석(기능형)에 대해 알려드렸습니다. 여러분의 도움이 되길 바랍니다. 어떤 질문이나 의문이 있으시면, 댓글을 남겨 주시면, 저는 즉시 답변드리겠습니다. 또한, 나락 교본에 대한 여러분의 지지에 깊이 감사드립니다!
성명: 이 문서의 내용은 인터넷에서 가져왔으며, 저작권은 원저자에게 있으며, 인터넷 사용자가 자발적으로 기여하고 업로드한 내용입니다. 이 사이트는 저작권을 소유하지 않으며, 인공 편집을 하지 않았으며, 관련 법적 책임도 부담하지 않습니다. 저작권 위반 내용을 발견하시면, notice#w 이메일로 발송해 주시기 바랍니다.3codebox.com(이메일 보내는 경우, #을 @으로 변경하시고, 관련 증거를 제공하여 신고해 주세요. 일단 사실로 확인되면, 이 사이트는 즉시 위반 내용을 삭제합니다.)