Wednesday, July 27, 2022

Panen data di twitter menggunakan python

Tidak sedikit penelitian di bidang text mining dan NLP (Natural Language Processing) memanfaatkan data dari platform populer twitter. Pada artikel kali, saya akan share bagaimana membuat dataset dari twitter menggunakan library tweepy di python.

Twitter adalah social media, dimana pengguna memungkinkan share pesan singkat sebanyak maksimal 140 karakter yang biasa disebut dengan tweet. Karena tweet dikirim terus menerus, Twitter merupakan cara yang tepat untuk mengetahui peristiwa/tren terkini. Biasanya pengguna menggunakan hashtag (#) dengan keyword tertentu dalam mengkategorikan topik apa yang mereka share.

Disini saya dibahas bagaimana mendapatkan data dari twitter yang akan disimpan ke dalam file csv dan database MySQL. Sebelumnya anda perlu membaca dokumentasi library tweepy disini: https://docs.tweepy.org/en/stable/. Selanjutnya kita perlu mendaftar sebagai developer di Twitter API untuk mendapatkan Api key dan Secret key. 

Mendapatkan API key Twitter

  1. Buka halaman https://developer.twitter.com/en,
  2. Login atau registrasi jika belum mempunyai akun developer,
  3. Buka menu Application Console dan create new Twitter application,
  4. Isi semua form dan check Developer agreement.
  5. Selanjutnya anda akan mendapat credential berupa Api key, Api Secret, Access token dan Access token secret

Setup Tweepy

Jika environment di python anda belum terinstall tweepy, bisa anda lakukan instalasi library tersebut dengan perintah: pip install tweepy

Untuk melakukan koneksi dan autentifikasi ke twitter, gunakan code berikut ini dimana variabel consumer_key, consumer_secret, access_token dan access_token_secret yang sudah anda isikan dengan data dari application yang sudah anda create diatas:


auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_token_secret)


Berikutnya kita perlu siapkan listerner untuk menangkap tweet yang di posting saat ini oleh pengguna berdasarkan keyword yang kita definisikan, sebagai contoh kita gunakan "jogja" dan "jogja istimewa". Pertama buat class StreamListener() selanjutnya jalankan class tersebut untuk mulai menarik data. Berikut code di python:

class StreamListener(tweepy.StreamListener):
def on_connect(self):
print("You are now connected to stream API")

def on_error(self, status_code):
print("An error has occured: " + repr(status_code))
return false

def on_status(self, status):
print(status.text)

stream_listener = StreamListener()
stream = tweepy.Stream(auth=auth, listener=stream_listener)
stream.filter(languages=['in'], track=["jogja", "jogja istimewa"])


Simpan file tersbut ke dengan nama "stream.py". Berikutnya bisa kita jalankan dari terminal atau command dengan perintah: python stream.py Bisa dilihat output di terminal tweet yang ditangkap oleh program tersebut. Untuk mengakhiri proses streamming tweet bisa menggunakan Ctrl + C atau close terminal / command aktif anda.

Menyimpan ke format CSV

Untuk menyimpan output tweet ke dalam csv, kita gunakan library csv dari python. Kemudian pada method on_status( ) di class StreamListener( ) perlu kita update dengan menambahkan kode untuk menulis ke file csv berikut ini:

with open('tweet_output.csv', 'a') as f:
    writer = csv.writer(f)
    writer.writerow([date, loc, name, desc, text, status.entities.get('hashtags')])
    time.sleep(10)


Pada kode diatas akan menulis output di file "tweet_output.csv" dengan kolom date, loc, name, desc, text, dan hashtags dari tweet yang dipisahkan dengan separator koma (,).

Berikut ini kode lengkap nya

import time
import tweepy
import csv

consumer_key = "<consumer key anda>"
consumer_secret = "<consumer secret anda>"
access_token = "<access token anda>"
access_token_secret = "<access token secret anda>"

class StreamListener(tweepy.StreamListener):
def on_connect(self):
print("You are now connected to stream API")

def on_error(self, status_code):
print("An error has occured: " + repr(status_code))
return false

def on_status(self, status):
text = status.text

# Menghilangkan karakter yg mungkin membuat error separator csv
remove_characters = [",", "\n"]
for c in remove_characters:
text.replace(c, " ")

try:
name = status.user.screen_name
desc = status.user.description
loc = status.user.location
date = status.created_at

with open('tweet_output.csv', 'a') as f:
writer = csv.writer(f)
writer.writerow([date, loc, name, desc, text, status.entities.get('hashtags')])
time.sleep(10)


except Exception as e:
print(e)

with open('tweet_output.csv', 'w') as f:
writer = csv.writer(f)
writer.writerow(['Date', 'Location','Name', 'Desc', 'Text', 'Hashtag'])


auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_token_secret)

listener = StreamListener(api=tweepy.API(wait_on_rate_limit=True))
streamer = tweepy.Stream(auth=auth, listener=listener)

print("Tracking data twitter on progress..")
streamer.filter(languages=['in'], track=['jogja','#jogja'])


Pada kode diatas di bagian stream.filter( ) ditambahkan parameter languages=['in'] untuk mengambil tweet yang menggunakan bahasa indonesia saja. Sedangkan untuk parameter track, bisa digunakan lebih dari 2 keyword.

Menyimpan ke database MySQL

Sedangkan untuk menyimpan data output tweet ke database MySQL, digunakan library pymysql. Selanjutnya kita membutuhkan satu method untuk melakukan koneksi dan penyimpanan ke tabel MySQL. Berikut ini kode dari method store_data( ) yang digunakan untuk menyimpan ke tabel di database:

def store_data(id, name, date, txt):
connection = pymysql.connect(host=host, user=user, password=password,
db=db, charset=charset, cursorclass=cursorclass)

try:
with connection.cursor() as cursor:
sql_save = "insert into tweet (tweet_id, screen_name, created_at, text) "
            sql_save += "values(%s, %s, %s, %s)"
cursor.execute(sql_save, (id, name, date, txt))
connection.commit()

cursor.close()
return
finally:
connection.close()


Dari kode diatas terlihat variabel connection digunakan untuk membuat koneksi ke database, variabel sql_save untuk menyimpan perintah query insert dan cursor.execute() untuk menjalankan perintah query tersebut. Untuk menerapkan di Streamer, pada method on_status() kita tambahkan pemanggilan method store_data() ini.

Sebelum menjalankan kode python tersebut di terminal, kita perlu membuat tabel di database MySQL terlebih dahulu. Berikut ini DDL untuk membuat kode di database dengan nama tabel "tweets".

CREATE TABLE `tweets` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `tweet_id` varchar(250) DEFAULT NULL,

  `screen_name` varchar(128) DEFAULT NULL,

  `created_at` timestamp NULL DEFAULT NULL,

  `text` text DEFAULT NULL,

  PRIMARY KEY (`id`)

) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8


Berikut ini kode lengkap pada file "stream-db.py" yang bisa anda jalankan.

import pymysql.cursors
import tweepy

host = 'localhost'
user = 'root'
password = ''
db = 'tweet'
charset = 'utf8'
cursorclass = pymysql.cursors.DictCursor

consumer_key = "<consumer key anda>"
consumer_secret = "<consumer secret anda>"
access_token = "<access token anda>"
access_token_secret = "<access token secret anda>"

def store_data(id, name, date, txt):
connection = pymysql.connect(host=host, user=user, password=password,
db=db, charset=charset, cursorclass=cursorclass)

try:
with connection.cursor() as cursor:
sql_save = "insert into tweets (tweet_id, screen_name, created_at, text) "
            sql_save += "values(%s, %s, %s, %s)"
cursor.execute(sql_save, (id, name, date, txt))
connection.commit()

cursor.close()
return
finally:
connection.close()

class StreamListener(tweepy.StreamListener):
def on_connect(self):
print("You are now connected to stream API")

def on_error(self, status_code):
print("An error has occured: " + repr(status_code))
return false

def on_status(self, status):
text = status.text
remove_characters = [",", "\n"]
for c in remove_characters:
text.replace(c, " ")

try:
name = status.user.screen_name
tid = status.id
date = status.created_at

store_data(tid, name, date, text)

except Exception as e:
print(e)

auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_token_secret)

listener = StreamListener(api=tweepy.API(wait_on_rate_limit=True))
streamer = tweepy.Stream(auth=auth, listener=listener)

print("Tracking data twitter on progress..")
streamer.filter(languages=['in'], track=['jogja', '#jogja'])


Selanjutnya bisa anda jalankan kode diatas dengan perintah python stream-db.py

Saat proses streamming berjalan, kemungkinan terdapat bebeapa warning message di terminal / command dikarenakan terdapat beberapa karekter yang tidak sesuai.  Hal ini bisa kita atasi dengan menambahkan beberapa filter di kode sebelum menyimpan di database.

Untuk mendapatkan data dalam jumlah besar, kita harus menjalankan stream tersebut secara terus-menerus. Pada artikel berikutnya inshaAllah akan saya bahas bagaimana membuat service stream tweepy di server Amazon AWS.


Tuesday, July 12, 2022

Sistem rekomendasi Content-based Filtering menggunakan PHP - MySQL (part 2)

Artikel berikut ini merupakan kelanjutan dari posting sebelumnya tentang implementasi sistem rekomendasi berbasis konten (Content-based filtering Recommender System) pada PHP. Silahkan bisa menyimak terlebih dahulu artikel bagian pertama disini: Sistem rekomendasi Content-based Filtering menggunakan PHP - MySQL (part 1).

Pada project ini, saya menggunakan library Sastrawi untuk melakukan pre-processing data, dan menggunakan library Nette/Database untuk akses ke database mysql, bisa anda cek dokumentasi lengkap di sini: https://doc.nette.org/en/database/core. Dengan menggunakan composer, kita bisa tambahkan code berikut di file composer.json:

{
"require": {
"sastrawi/sastrawi": "^1.2",
"nette/database": "^3.1"
}
}


Kemudian bisa dijalankan perintah composer update pada terminal untuk mendownload beberapa library diatas ke project kita.

Pertama kita siapkan class di PHP untuk menghitung TF-IDF dan Cosine similarity seperti yang sudah dibahas di artikel bagian 1. Kali ini, saya menggunakan nama class Cbrs dan nama file disesuaikan menjadi Cbrs.php. Berikut ini kode lengkap dari class tersebut.

Selanjutnya, kita siapkan file index.php untuk menampilkan daftar list hotel secara random dari tabel di database. Untuk tampilan HTML digunakan Bootstrap CSS supaya lebih rapi. Dan pada bagian akhir, kita buat file detail.php dimana akan menampilkan 1 hotel yang dipilih, dan kemudian akan di cari similarity/kesamaan dari sejumlah hotel dengan score paling besar berdasarkan id hotel yang dipilih tersebut.

Pada kode diatas, terdapat function pre_process() dan get_hotel_detail(), dimana kegunaan fungsi pertama untuk melakukan pembersihan data sebelum masuk ke class perhitungan TF-IDF dan cosine similarity di class Cbrs dan fungsi kedua untuk menampilkan detail hotel berdasarkan parameter id. Variabel $n digunakan untuk menyimpan jumlah item rekomendasi yang akan ditampilkan (top-n). Dan pada bagian perulangan variabel $r ditambahkan keyword continue; untuk menghilangkan item kunci, dimana nilainya = 1 dikarenakan menghitung similarity dari item yang sama. Selain itu, digunakan juga keyword break; untuk keluar dari perulangan jika jumlah n rekomendasi sesuai dengan variabel $n.

Tampilan antar muka detail hotel dan rekomendasi 8 item (beserta score similarity)

Jika anda membutuhkan kode lengkap dari project diatas beserta file database sql, silahkan tuliskan alamat email di bagian komentar. InshaAllah akan segera saya kirimkan ke email anda.


Friday, July 8, 2022

Sistem rekomendasi Content-based Filtering menggunakan PHP - MySQL (part 1)

Sebelum menyimak dan mengikuti tutorial dalam membangun sistem rekomendasi berbasis konten (content-based filtering) di artikel ini, sebaiknya membaca terlebih dahulu 2 artikel yang saya tulis sebelumnya, karena hal tersebut berkaitan dan saya menggunakan case/domain yang sama seperti sebelumnya.

Berikut artikel yang berkaitan dengan posting ini:

  1. Preprocessing text Bahasa Indonesia di PHP dengan Sastrawi
  2. Menghitung TF-IDF menggunakan kode PHP
Domain item yang akan saya bahas disini adalah data hotel, seperti pada artikel-artikel sebelumnya. Tahapan dalam membangun aplikasi sistem rekomendasi ini diawali dengan melakukan preprocesisng menggunakan library Sastrawi yang ada di PHP, dilanjutkan dengan melakukan pembobotan kata menggunakan metode TF-IDF dan terakhir adalah mengukur jarak kedekatan(similarty) dari 1 item ke item lainnya.

Pada artikel ini akan saya bahas bagian terakhir, yaitu menghitung similarity antar item menggunakan metode cosine similarity dan membuat top-n rekomendasi berdasarkan item yang dipilih. Kedua tahapan sebelumnya sudah ada di artikel yang saya sertakan link diatas 👆.

Cosine similarity

Untuk memudahkan pemahaman cara kerja cosine similarity, saya akan gunakan Spreadsheet untuk perhitungan nya dengan menggunakan contoh data hotel seperti pada case di artikel sebelumnya. Pada contoh data ini terdapat 3 dokumen dan 10 terms/kata yang akan kita ujicoba untuk perhitungan dan sudah kita beri bobot untuk masing-masing terms. Selanjutnya untuk menghitung similarity 2 item kita gunakan rumus berikut:

Pertama kita akan hitung terlebih dahulu sumproduct dari item A dan B, selanutnya kita bagian dengan perkalian panjang vektor A dan B. Pada Spreadsheet kita bisa gunakan formula SUMPRODUCT() sendangkan panjang vektor kita gunakan rumus SQRT(SUMSQ()).

Hasil dari pembobotan kata pada setiap dokumen

Berikut ini contoh rumus dari file Spreadsheet diatas untuk mencari similarity dari item d1 dan d2:

=SUMPRODUCT(C17:L17,C18:L18)/(SQRT(SUMSQ(C17:L17))*SQRT(SUMSQ(C18:L18)))

Selanjutnya kita bisa buat matrix untuk menghitung setiap dokumen. Score cosine similarity mempunyai rentang dari 0 - 1, yang artinya nilai mendekati 1 bearti kedua item tersebut sangat mirip, sedangkan mendekati 0 artinya kedua item tidak mirip. Perhitungan kedua item yang sama akan menghasilkan nilai 1.

Matrix perhitungan similaity antar item

Silahkan tuliskan alamat email anda di bagian komentar jika ingin mendapatkan file Spreadsheet untuk perhitungan seperti pada gambar diatas.

Cosine similarity di PHP

Berdasarkan perhitungan dari Spreadsheet dan rumus diatas, di PHP kita dapat membuat fungsi baru untuk mencari similarity antara 2 item dan mengurutkan hasil kemiripan score dari yang paling besar.

# Fungsi menghitung similarity ke semua dokumen
# parameter input = id dari item
public function similarity($d1){
    $score = [];
    foreach($this->doc_weight as $ndw => $w){
        $score[$ndw] = $this->cosim($d1, $ndw);
    }
    
    arsort($score);
    return $score;
}

private function cosim($d1, $d2){
    $dw = $this->doc_weight;
    
    # sum square dari 2 doc
    $dw1 = $dw[$d1];
    $dw2 = $dw[$d2];
    
    $dx = 0;
    $dx1 = 0;
    $dx2 = 0;
    
    foreach($this->corpus_terms as $t => $terms){
        $dx += $dw1[$t] * $dw2[$t];
        $dx1 += $dw1[$t]*$dw1[$t];
        $dx2 += $dw2[$t]*$dw2[$t]; 
    }
    
    return round($dx / (sqrt($dx1) * sqrt($dx2)), 4);
    
}

Kedua fungsi diatas merupakan kelanjutan kode dari Tfidf.php pada artikel berikut ini: Menghitung TFIDF menggunakan kode PHP, yang perlu anda tambahkan di class Tfidf. Selanjutnya pada file index.php anda bisa menjalankan fungsi tersebut dengan menggunakan parameter index array sebagai dokumen kunci yang akan dicari kemiripannya.

$hotel = array();
$hotel[1] = "Hotel Modern yang Terjangkau";
$hotel[2] = "Akomodasi modern, nyaman, dan tenang";
$hotel[3] = "Hotel bintang 3 yang mewah namun dengan harga yang terjangkau";

foreach($hotel as $key=>$item){
    $hotel[$key] = pre_process($item);
}

$cbrs = new Tfidf();
$cbrs->create_index($hotel);
$cbrs->idf();
$w = $cbrs->weight();  
$r = $cbrs->similarity(1);

print '<pre>';
print_r($r);
print '</pre>';

Dari kode diatas akan menghasilkan score kemiripan dari dokumen kunci(index array) sesuai dengan parameter yang di inputkan pada fungsi similarity.

Array
(
    [1] => 1
    [3] => 0.2062
    [2] => 0.1203
)

Pada posting berikutnya akan saya bahas detail bagaimana menggunakan dataset yang sudah tersimpan di tabel pada database MySQL untuk menampilkan item rekomendasi dari dokumen yang kita pilih.



Friday, July 1, 2022

Menghitung TF-IDF menggunakan kode PHP


Term Frequency, Inverse Document Frequency (TFIDF), merupakan sub-area dari Natural Language Processing(NLP) yang digunakan dalam information retrievel dengan tujuan untuk ekstrasi data. Sederhananya, bagaimana menghitung kemunculan setiap kata dalam dokumen dan memberi bobot pada setiap kata, dan menghitung skor untuk dokumen tersebut.

Sebelum kita bahas code di PHP, saya akan share penjelasan perhitungan manual TF-IDF menggunakan Spreadsheet atau Excel untuk memudahkan memahami cara kerjanya. Harapannya, anda bisa melakukan improvisasi code di bahasa pemrograman yang lainnya.

Pastikan sebelum mulai proses pembobotan kata dengan metode ini, text yang anda gunakan sudah melalui tahap preprocessing. Untuk preprocessing text di bahasa indonesia anda bisa menggunakan library sastrawi yang sudah saya bahas di artikel berikut ini: https://blog.ariflaksito.net/2022/04/preprocessing-text-bahasa-indonesia-di.html.

Berikut ini perhitungan di Spreadsheet menggunakan 3 dokumen seperti yang saya gunakan pada artikel preprocessing pada link artikel diatas.


Kolom token/term merupakan kata unique dari semua dokumen yang ada. Selanjutnya pada kolom tf, dibagi menjadi 3 dokumen untuk menghitung jumlah setiap kata yang ada di masing-masing dokumen. Pada kolom df akan menghitung jumlah kata yang muncul pada semua dokumen. Pada bagian ini kita hitung jumlah kemunculan, bukan jumlah total kata (digunakan rumus countif bukan sum). Kolom D/df merupakan pembagian dari jumlah dokumen (n) dan df. Jumlah dokumen yang kita gunakan untuk contoh perhitungan ini adalah 3.

Kolom IDF merupakan nilai log dari D/df, dan terkahir score tf-idf merupakan perkalian dari tf masing-masing dokumen dan token dengan nilai IDF. Hasilnya akan berupa matrix bobot dari dokumen dan term seperti pada gambar diatas.

Bisa anda tuliskan email di komentar jika menghendaki file Spreadsheet/Excel untuk contoh perhitungan  TF-IDF diatas.

Selanjutnya kita masuk pada inti pembahasan, yaitu membuat script PHP untuk perhitungan bobot kata seperti cara di Excel diatas. Pertama siapkan class dan beberapa fungsi di PHP, yaitu:

  • fungsi untuk mebuat index dari setiap kata
  • fungsi untuk menghitung idf
  • fungsi untuk menghitung weight/tf-idf

Terdapat 2 file pada code diatas, index.php dan Tfidf.php. Dimana fungsi-fungsi TF-IDF berada pada class Tfidf() di file Tfidf.php. Sedangkan file index.php digunakan sebagai input dokumen untuk menjalankan perhitungan tersebut.

Pada fungsi create_index() di class Tidf menjalankan proses untuk melakukan split setiap kata dan memberi informasi posisi dari setiap dokumen. Hasilnya bisa di lihat menggunakan fungsi show_index(). Fungsi idf() digunakan untuk menghitung menggunakan rumus log(D/df). Dan fungsi weight() untuk menghitung score/nilai dari tf-idf dari setiap kata/term di dokumen. Hasilnya berupa matrix/array dengan bobot pada setiap term di dokumen.

Berikut ini hasil dari kode tersebut, sama seperti pada perhitungan di excel diatas pastinya..

Array
(
    [1] => Array
        (
            [hotel] => 0.1761
            [modern] => 0.1761
            [jangkau] => 0.1761
            [akomodasi] => 0
            [nyaman] => 0
            [tenang] => 0
            [bintang] => 0
            [3] => 0
            [mewah] => 0
            [harga] => 0
        )

    [2] => Array
        (
            [hotel] => 0
            [modern] => 0.1761
            [jangkau] => 0
            [akomodasi] => 0.4771
            [nyaman] => 0.4771
            [tenang] => 0.4771
            [bintang] => 0
            [3] => 0
            [mewah] => 0
            [harga] => 0
        )

    [3] => Array
        (
            [hotel] => 0.1761
            [modern] => 0
            [jangkau] => 0.1761
            [akomodasi] => 0
            [nyaman] => 0
            [tenang] => 0
            [bintang] => 0.4771
            [3] => 0.4771
            [mewah] => 0.4771
            [harga] => 0.4771
        )

)

Referensi

  1. https://khozaimi.wordpress.com/2010/06/26/menghitung-tf-idf-engan-php/
  2. https://www.youtube.com/watch?v=B3UZ8DxHocQ&t=1s

Preprocessing text bahasa Indonesia di PHP dengan Sastrawi

 

the image was taken from https://www.analyticsvidhya.com

Bismillah.. setelah sekian lama belum ada artikel lagi, semoga bisa kembali produktif menulis di blog ini lagi dan terus konsisten  ✌.

Pada beberapa artikel sebelumnya terkait content-based filtering di Sistem rekomendasi, saya menggunakan salah satu library untuk preprocessing text yaitu Sastrawi di Python. Ternyata beberapa mahasiswa menanyakan penggunaan library tersebut di pemrograman PHP. Inilah salah satu alasan adanya artikel ini.

Source code dari Sastrawi bisa anda temukan disini: https://github.com/sastrawi/sastrawi. Library ini sudah ada pada beberapa bahasa pemrograman, seperti: Java, C, Python, Go, Rudy dan tentunya di PHP. Untuk menggunakan di PHP anda bisa melakukan instalasi menggunakan Composer. Apa itu composer? Silahkan beca artikel berikut ini: https://blog.ariflaksito.net/2018/08/berkenalan-dengan-composer.html

Pada terminal atau command line anda bisa menjalankan perintah berikut ini:
php composer.phar require sastrawi/sastrawi:^1

Library ini dapat digunakan untuk melakukan stemming di bahasa Indonesia, yaitu proses untuk menemukan kata dasar dari sebuat kata, dengan menghilangkan semua imbuhan termasuk dari awalan, sisipan, akhiran dan kombinasi dari awalan dan akhiran pada kata turunan.

Contohnya:

  • Menghilang = hilang
  • Terjangkau = jangkau
Selain itu, Sastrawi dapat juga digunakan untuk menghilangkan kata-kata yang memiliki arti tidak penting(stop-words) dalam proses pengolahan text, seperti: dan, atau, yang, dll.

Berikut ini code di PHP berupa fungsi untuk melakukan stemming dan menghapus stop-words:

Output dari code diatas sebagai berikut:

Array
(
    [1] => hotel modern jangkau
    [2] => akomodasi modern nyaman tenang
    [3] => hotel bintang 3 mewah harga jangkau
)

Dari output tersebut terlihat bahwa beberapa kata stop-words dan tanda koma (,) berhasil di hapus dan kata "terjangkau" diubah menjadi bentuk kata dasar "jangkau". Tahapan ini berguna untuk beberapa case berikutnya seperti: text classification, sentiment analysis atau yang akan saya bahas berikutnya yaitu menghitung pembobotan kata dengan TF-IDF untuk sistem rekomendasi berbasis content (content-based filtering).

Selamat mencoba..

Referensi

  1. https://adinyahya.com/cara-penerapan-stemming-dengan-menggunakan-library-sastrawi/
  2. https://github.com/sastrawi/sastrawi