Unicode normalization (form C) in R : convert all characters with accents into their one-unicode-character form?
Ok, it appears that a package has been developed to enhance and simplify the string manipulation toolbox in R (finally!). It is called stringi and looks very promising. Its documentation is very well written, and in particular I find the pages about encodings and locales much more enlightening than some of the standard R documentation on the subject.
It has Unicode normalization functions, as I was looking for (here form C):
> stri_trans_nfc('\u00e9') == stri_trans_nfc('\u0065\u0301')
[1] TRUE
It also contains a smart comparison function which integrates these normalization questions and lessens the pain of having to think about them:
> stri_compare('\u00e9', '\u0065\u0301')
[1] 0
# i.e. equal ;
# otherwise it returns 1 or -1, i.e. greater or lesser, in the alphabetic order.
Thanks to the developers, Marek Gągolewski and Bartek Tartanus, and to Kurt Hornik for the info!
How to convert one unicode char to second looking exactly the same?
What you're describing is Unicode canonical equivalence.
I figured using utf8mb4_unicode_ci
collation would solve this for you. However the documentation implied that it would not:
A combined character will be considered different from the same character written with a single unicode character in string comparisons, and the two characters are considered to have a different length (for example, as returned by the CHAR_LENGTH() function or in result set metadata).
However a quick test seems to indicate that is incorrect:
mysql -u root -e "SELECT 'a<0301>' = 'á' COLLATE utf8mb4_unicode_ci;"
+-----------------------------------------+
| 'á' = 'á' COLLATE utf8mb4_unicode_ci |
+-----------------------------------------+
| 1 |
+-----------------------------------------+
Confusing.. though I wonder if that sentence is only applies in the context of the previous two sentences:
Also, combining marks are not fully supported. This affects primarily Vietnamese, Yoruba, and some smaller languages such as Navajo.
So anyway, that may work for you. It is worth noting that utf8mb4_unicode_ci
will result in relatively loose matching, e.g. á
and a
will be treated equivalent:
mysql -u root -e "SELECT 'á' = 'a' COLLATE utf8mb4_unicode_ci;"
+---------------------------------------+
| 'á' = 'a' COLLATE utf8mb4_unicode_ci |
+---------------------------------------+
| 1 |
+---------------------------------------+
Another option, should you wish to have finer control on this, is to normalize text before insert into your database (intl extention required). Whether or not you'll want to do this depends on how interested you are in keeping it in it's absolute original form. The normalization process guarantees visual equivalence, so it should be safe to apply. For example, if you were to normalize to the composed form (which would be most storage efficient, should you care):
<?php
$a = 'á'; // 0xC3 0xA1
$b = 'á'; // 0x61 0xCC 0x81
$ca = \Normalizer::normalize($a, \Normalizer::FORM_C);
$cb = \Normalizer::normalize($b, \Normalizer::FORM_C);
$da = \Normalizer::normalize($a, \Normalizer::FORM_D);
$db = \Normalizer::normalize($b, \Normalizer::FORM_D);
var_dump($a === $b); // FALSE
var_dump($a === $cb); // TRUE, $a is already composed
var_dump($ca === $cb); // TRUE, $a is unchanged by normalizer
var_dump($b === $da); // TRUE, $b is already decomposed
var_dump($db === $da); // TRUE, $b is unchanged by normalizer
Umlaut matching in R regex
"K<c3><a4>se"
encodes the "ä" as unicode character U+00E4 (LATIN SMALL LETTER A WITH DIAERESIS).
"Ka<cc><88>se"
encodes the "ä" as unicode characters U+0061 (LATIN SMALL LETTER A) and U+0308 (COMBINING DIAERESIS).
Both are technically correct, but distinct. To compare them, you will need to normalize the strings. You could use the package stringi:
stri_trans_nfc("Ka\u0308se") -> "K\u00E4se"
More information:
- Unicode normalization (form C) in R : convert all characters with accents into their one-unicode-character form?
- Wikipedia: Unicode Equivalence
R - Special characters reading with read_fwf
You can fix this by changing the encoding via locale
to LATIN1
:
library(readr)
fw <- fwf_widths(c(2,13,2), col_names = c('A','B','C'))
x <- read_fwf('00StackOvérflow00\n',
col_positions = fw, locale = locale(encoding = 'LATIN1'))
Returning:
# A tibble: 1 x 3
A B C
<chr> <chr> <chr>
1 00 StackOvérflow 00
Convert special letters to english letters in R
You can use chartr
x <- "ØxxÅxx"
chartr("ØÅ", "OA", x)
[1] "OxxAxx"
And/or gsub
y <- "Æabc"
gsub("Æ", "AE", y)
[1] "AEabc"
What is the best way to remove accents (normalize) in a Python unicode string?
How about this:
import unicodedata
def strip_accents(s):
return ''.join(c for c in unicodedata.normalize('NFD', s)
if unicodedata.category(c) != 'Mn')
This works on greek letters, too:
>>> strip_accents(u"A \u00c0 \u0394 \u038E")
u'A A \u0394 \u03a5'
>>>
The character category "Mn" stands for Nonspacing_Mark
, which is similar to unicodedata.combining in MiniQuark's answer (I didn't think of unicodedata.combining, but it is probably the better solution, because it's more explicit).
And keep in mind, these manipulations may significantly alter the meaning of the text. Accents, Umlauts etc. are not "decoration".
Is it possible to convert between Unicode normal forms in PHP?
Unicode normalization is provided by the intl
extension and its Normalizer
class.
http://docs.php.net/manual/en/class.normalizer.php
Greek vowels with accents shown as two characters instead of a single one
The root cause: Sometime there is many different ways to represent the same glyph with Unicode. Usually we convert to a canonical form, but there is two canonical/normalization form (decomposed: NFD and composed: NFC). Apple prefers the first (and it was the original prefered way of Unicode), most of the other operating systems prefer the second. And each font has own preference (but shaper library will handle it).
You can transform your text into the canonical composed form (NFC), but not all glyphs can be transformed into one single characters: some combination of accent and base character requires two codepoints (or more if you have multiple accents).
Related Topics
How to Run Lm Regression for Every Column in R
How to Add an Inset (Subplot) to "Topright" of an R Plot
Reading Hdf Files into R and Converting Them to Geotiff Rasters
R: Losing Column Names When Adding Rows to an Empty Data Frame
Adjusting Width of Tables Made with Kable() in Rmarkdown Documents
Simple Frequency Tables Using Data.Table
How Can Put Multiple Plots Side-By-Side in Shiny R
How to Use Outlier Tests in R Code
Ggplot2: Is There a Fix for Jagged, Poor-Quality Text Produced by Geom_Text()
Remove Rows Where All Variables Are Na Using Dplyr
Extracting Coefficient Variable Names from Glmnet into a Data.Frame
How to Add Another Layer/New Series to a Ggplot
Ggplot Geom_Point() with Colors Based on Specific, Discrete Values