Archive for the 'Numbers 4' Category

2017/10/26 Numbersで選択中の範囲を横方向に比較しカラム間の差分検出してセルの色変更 v2

Numbersの書類上で選択中の範囲(横に2列)を横方向に比較し、変更のあったセルの色を変更するAppleScriptです。

2017-10-25-23_44_59.gif

実行にはShane StanleyのAppleScript Library「BridgePlus」のインストールが必要です。Numbersの選択範囲から返ってくる1D Arrayを2D Arrayに変換するために使用しています。

numb1_resized.png
▲実行前。Numbers書類上のアクティブなシートの最初のテーブルが処理対象

numb2_resized.png
▲実行後。差分のあったセルを赤くマーク

Numbersの選択セル範囲を取得するのに文字列を計算するのではなく、Numbersの機能を使って手軽に行うように変更しました。最近、ちょっとCocoa風に処理するのに慣れてしまって、アプリケーションの内蔵機能を呼び出したほうがシンプルに書ける場合にはそうすべきでしょう。

処理速度については、対象データがそれほど大きくないことを前提として書いたので、データ量が多くなった場合には(数千行以上)急に遅くなるかもしれません。

ただ、まいどまいど思いますが、Numbersで選択中のTableの情報を取得できないのはダメすぎです。

それにしても、辞書.appの辞書名をチョロチョロ変えるのは勘弁してほしいところです(→スクリーンショット)。無意味な名称変更がまんべんなく、、、、、

AppleScript名:Numbersで選択中の範囲を横方向に比較しカラム間の差分検出してセルの色変更 v2
– Created 2016-10-26 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use bPlus : script “BridgePlus”
–http://piyocast.com/as/archives/4929

load framework

tell application “Numbers”
  tell front document
    tell active sheet
      set tCount to count every table
      
if tCount is not equal to 1 then
        display notification “Wrong Table number (not one) in current sheet.”
        
return
      end if
      
      
tell table 1
        set aSel to properties of selection range
        
set selName to name of aSel
        
set {s1, s2} to parseByDelim(selName, “:”) of me
        
        
–始点の情報を取得する
        
set s1Row to (address of row of range s1) as integer
        
set s1Column to (address of column of range s1) as integer
        
        
–終点の情報を取得する
        
set s2Row to (address of row of range s2) as integer
        
set s2Column to (address of column of range s2) as integer
        
        
–選択範囲の情報を取得する
        
set aHeight to s2Row - s1Row + 1 –高さ(Height of selection range)
        
set aWidth to s2Column - s1Column + 1 –幅(Width of selection range)
        
        
set dList to value of every cell of selection range –Every Data from Selection (1D Array)
        
set diffList to getDiffAddressList(dList, aWidth) of me
        
        
repeat with i in diffList
          copy i to {adrX, adrY}
          
          
set adrX to adrX + s1Column
          
set adrY to adrY + s1Row - 1
          
          
tell row adrY
            tell cell adrX
              –Async Mode Execution (x2 Speed)
              
ignoring application responses
                set text color to {65535, 0, 65535}
              end ignoring
            end tell
          end tell
          
        end repeat
        
      end tell
    end tell
  end tell
end tell

on getDiffAddressList(dList, aWidth)
  set d2List to (current application’s SMSForder’s subarraysFrom:(dList) groupedBy:aWidth |error|:(missing value)) as list
  
set diffList to {}
  
set diffAdrList to {}
  
  
set yOffsetCount to 1
  
  
repeat with i in d2List
    set i1 to first item of i
    
set i2 to rest of i
    
    
set xOffsetCount to 1
    
    
repeat with ii in i2
      set jj to contents of ii
      
if i1 is not equal to jj then
        set the end of diffList to {i1, jj}
        
set the end of diffAdrList to {xOffsetCount, yOffsetCount}
        
exit repeat
      end if
      
      
set xOffsetCount to xOffsetCount + 1
      
    end repeat
    
    
set yOffsetCount to yOffsetCount + 1
  end repeat
  
  
return diffAdrList
end getDiffAddressList

on parseByDelim(aData, aDelim)
  set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to aDelim
  
set dList to text items of aData
  
set AppleScript’s text item delimiters to curDelim
  
return dList
end parseByDelim

★Click Here to Open This Script 

2017/10/26 Numbersの横セルのアドレス文字列(26進数)を10進数リストに変換

Numbersのセルのアドレス文字列を10進数のリストに変換するAppleScriptです。

Numbersのセル間のデータの比較を行おうとしたら、selection range(のname)が”A2:B12″といった形式で返ってきて、rangeの幅とか高さは自分で計算する必要があったので、それぞれのアドレス文字列を数値に変換する必要があると思われました(このあたり、何回も同じ処理を組んでいるような気がするのは、気のせい?)。

→ 6年前に組んだAppleScriptですでにもっと手軽に求める方法を実装してありました

そこで、”A2″とか”B12″といったアドレスの文字列を10進数のリストに変換してみることに。

アルファベットだけ、数字だけを抽出してそれぞれ処理しています。いつものとおり、ありあわせのルーチンを組み合わせただけで、あらたに作ったのはごく一部。

最終的に、Numbersの選択範囲の大きさを計算して、選択範囲から取得した1D List(ExcelとちがってNumbersは選択範囲のデータを取得すると連続した1D Listになるため)を選択範囲のデータ幅に合わせて2D Listに変換し、各行で差分がないかどうかチェックしました。

NSNotFoundの値が9223372036854775807になっているOS側のバグ(macOS 10.12.5〜10.12.6あたり?)に対応してあります。ちなみに、macOS 10.13.1の最新Betaではここだけは直っているものの、Web上で検索するとNSNotFoundの値の設定ミスはAppleがしょっちゅうカマしているようなので、対策し続けておいた方がよさそうです。

AppleScript名:Numbersの横セルのアドレス文字列(26進数)を10進数リストに変換
– Created 2017-10-25 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4923

property NSString : a reference to current application’s NSString
property NSArray : a reference to current application’s NSArray
property NSRegularExpressionSearch : a reference to current application’s NSRegularExpressionSearch

set {xColumn, yRow} to calcNumbersRangeNameToDecimalList(“B29″) of me
–> {2, 29}

set {xColumn, yRow} to calcNumbersRangeNameToDecimalList(“AZ12″) of me
–> {52, 12}

on calcNumbersRangeNameToDecimalList(aData)
  set aRes to returnAlphabetOnly(aData) of me
  
set bRes to returnNumberOnly(aData) of me
  
set a2Res to numbersAddrToDecimal(aRes) of me
  
return {a2Res as integer, bRes as integer}
end calcNumbersRangeNameToDecimalList

–文字列から数字だけを抽出して返す
on returnNumberOnly(aStr)
  set anNSString to NSString’s stringWithString:aStr
  
set anNSString to anNSString’s stringByReplacingOccurrencesOfString:“[^0-9]” withString:“” options:(NSRegularExpressionSearch) range:{0, anNSString’s |length|()}
  
return anNSString as text
end returnNumberOnly

–文字列からアルファベットだけを抽出して返す
on returnAlphabetOnly(aStr)
  set anNSString to NSString’s stringWithString:aStr
  
set anNSString to anNSString’s stringByReplacingOccurrencesOfString:“[^A-Za-z]” withString:“” options:(NSRegularExpressionSearch) range:{0, anNSString’s |length|()}
  
return anNSString as text
end returnAlphabetOnly

–Numbersの横方向アドレス(A〜Zの26進数)文字列を10進数に変換
on numbersAddrToDecimal(origStr)
  return aNthToDecimal(origStr, {“A”, “B”, “C”, “D”, “E”, “F”, “G”, “H”, “I”, “J”, “K”, “L”, “M”, “N”, “O”, “P”, “Q”, “R”, “S”, “T”, “U”, “V”, “W”, “X”, “Y”, “Z”}) of me
end numbersAddrToDecimal

–n進数文字列を10進数に変換する
on aNthToDecimal(origStr, nTh)
  set resNumber to 0
  
set sList to reverse of (characters of origStr)
  
set aLen to length of nTh
  
set digitCount to 0
  
  
repeat with i in sList
    set j to contents of i
    
set aRes to offsetInList(j, nTh) of me
    
    
if digitCount = 0 then
      set digitNum to 1
    else
      set digitNum to digitCount * aLen
    end if
    
    
set resNumber to resNumber + (aRes * digitNum)
    
set digitCount to digitCount + 1
  end repeat
  
  
return resNumber
end aNthToDecimal

on offsetInList(aChar, aList)
  set anArray to NSArray’s arrayWithArray:aList
  
set aInd to (anArray’s indexOfObject:aChar)
  
if aInd = current application’s NSNotFound or (aInd as number) > 9.99999999E+8 then
    error “Invalid Character Error”
  else
    return (aInd as integer) + 1 –0 to 1 based index conversion
  end if
end offsetInList

★Click Here to Open This Script 

2017/07/05 Numbersでアクセス解析データを合計 v3

Numbersで表示している最前面の書類のすべてのシート上にある表のデータを集計するAppleScriptです。

4年分ぐらいのBlogのアクセス集計を行うことになり、ページ単位のアクセス情報をNumbers上にペースト。

numbers1.png

月ごとにシートを分けて保存し、「アクセスURL」「アクセス回数」のデータを分析することにしました。データは月ごとにシートに分けているけれども、分析を行う際にはすべてをまとめる必要がある、というのと、Numbersにそんな便利な機能はないというのがミソ。こういう用途こそAppleScriptで自動化すべきものです。

アクセス解析ページのデータをNumbersにコピペで貼り付けて、集計自体はAppleScriptで実行。

Numbersからデータを取得する部分は割とさくっとできたのですが、問題は集計部分。

Cocoaの機能を使わないと処理が大変なことはわかっていたので、とりあえずNSCountedSetにページのURLを突っ込んで集計を試みましたが、NSCountedSetを他のデータから作るのは割と融通がききませんでした。

“A”が10回存在するというデータをNSCountedSetに突っ込もうとすると、

  {”A”, “A”, “A”, “A”, “A”, “A”, “A”, “A”, “A”, “A”}

という配列を作って追加しないといけないようだったので、1,000回だったら1,000個のデータを含む配列を作って……とやっていたら、要素数が100万個ぐらいの巨大な配列を作ることになり、処理時間が余計にかかってしまいました(逆に、メモリ8Gバイトのマシンでよく動いたものだと)。

結局、このバージョンのように集計時には1つの巨大なレコードにページのURLとアクセス回数を記録するように変更しました。

   {”/path/to/url1″:1024, “/path/to/url2″:311…….}

このあたり、AppleScriptのレコードではURLそのものをラベルにすることは(許容されない特殊文字を含んでいるため)不可能ですが、CocoaのNSDictionary(正確にはNSMutableDictionary)であればこのようなラベルを持てるので、便利に使っています。

集計後にURLと回数を個別に取り出して配列に入れ、回数で降順ソート。

  {{accessCount:300, aURL:”/path/to/url1″}, {accessCount:200, aURL:”/path/to/url2″}}

NSCountedSetで集計していたときには80秒ぐらいかかっていましたが、NSDictionaryで集計したら7秒程度(MacBook Pro Retina 2012)で終了しました。

集計元のNumbers書類(34シート、各シートに表1つ。表の行数はだいたい1,000行ぐらい)から集計対象データを取り出す部分は4秒ぐらいなので、集計部分は3秒程度かかっています。

このAppleScript自体はそれほど汎用性があるわけではありませんが、Numbersに入れたデータをAppleScriptから集計するのも、Cocoaの機能を利用すれば割と問題なく行えるという「見本」であります。

AppleScript名:Numbersでアクセス解析データを合計 v3
– Created 2017-07-04 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4716

script spd
  property allData : {}
end script

–初期化
set (allData of spd) to {}

–Numbers書類上の各シートの表の所定の範囲からデータを取り出す
tell application “Numbers”
  set dCount to count every document
  
if dCount = 0 then return
  
  
tell front document
    set sCount to count every sheet
    
repeat with i from 1 to sCount
      tell sheet i
        tell table 1
          set aRowCount to row count
          
set aRange to range (“A2:B” & (aRowCount as string))
          
set aDat to value of every cell of aRange
          
set (allData of spd) to (allData of spd) & aDat
        end tell
      end tell
    end repeat
  end tell
end tell

–取り出したデータを集計(1つの巨大なRecordに動的に項目を作成しつつ集計)
set aLen to length of (allData of spd)
set aDic to current application’s NSMutableDictionary’s alloc()’s init()

repeat with i from 1 to aLen by 2
  set aKey to contents of item i of (allData of spd)
  
set newVal to contents of item (i + 1) of (allData of spd)
  
  
—解析先ディレクトリをしぼりこみ
  
if aKey begins with “/as/archives/” then
    set aValue to (aDic’s valueForKey:aKey)
    
if aValue = missing value then
      (aDic’s setObject:newVal forKey:aKey) –新規エントリ作成
    else
      (aDic’s setObject:(newVal + aValue) forKey:aKey) –加算
    end if
  end if
end repeat

–集計したデータを個別要素に分離し、アクセス回数でソート
set anArray to current application’s NSMutableArray’s alloc()’s init()
set allKeys to (aDic’s allKeys()) as list

repeat with i in allKeys
  set bVal to (aDic’s valueForKey:i)
  
set bDic to {accessCount:(bVal as integer), aURL:i}
  (
anArray’s addObject:bDic)
end repeat

set bList to sortRecListByLabel(anArray, “accessCount”, false) of me –降順ソート
return bList as list

–リストに入れたレコードを、指定の属性ラベルの値でソート
on sortRecListByLabel(aRecList, aLabelStr as string, ascendF as boolean)
  set aArray to current application’s NSArray’s arrayWithArray:aRecList
  
set sortDesc to current application’s NSSortDescriptor’s alloc()’s initWithKey:aLabelStr ascending:ascendF
  
set sortDescArray to current application’s NSArray’s arrayWithObjects:sortDesc
  
set sortedArray to aArray’s sortedArrayUsingDescriptors:sortDescArray
  
  
return sortedArray
end sortRecListByLabel

★Click Here to Open This Script 

2017/03/28 Keynote/Pages/Numbersがアップデート

Keynote/Pages/Numbersがアップデートし、それぞれKeynote 7.1、Pages 6.1、Numbers 4.1になりました(要macOS 10.12)。

■Keynote 7.1.0
–AppleScript用語辞書の変更点
・current slideがread only属性ではなくなった
・image formatで指定できる値が変更された
small / medium / large –> 60p / 540p / 720p / 1080p / 2160p / native size
・movie formatで指定できる値が変更された
small / medium / large –> 360p / 540p / 720p / 1080p / 2160p / native size
–機能上の変更点
・PDF書き出しをmacOS 10.12.4上で行えるようになった(権限エラーが出なくなった)

■Pages 6.1.0
–AppleScript用語辞書の変更点
・exportコマンドで指定できるフォーマットとして「formatted text」が追加された(RTF?)
–機能上の変更点
・PDF書き出しをmacOS 10.12.4上で行えるようになった(権限エラーが出なくなった)