Пример #1
0
        private void WriteAttribute(ArffAttribute attribute, int indent)
        {
            string type = attribute.Type.ToString();

            if (indent != 0)
            {
                streamWriter.Write(new string(' ', indent));
            }

            streamWriter.WriteLine("@attribute {0} {1}", QuoteAndEscape(attribute.Name), type);

            if (attribute.Type is ArffRelationalAttribute relationalAttribute)
            {
                foreach (ArffAttribute childAttribute in relationalAttribute.ChildAttributes)
                {
                    WriteAttribute(childAttribute, indent + 2);
                }

                if (indent != 0)
                {
                    streamWriter.Write(new string(' ', indent));
                }

                streamWriter.WriteLine("@end {0}", QuoteAndEscape(attribute.Name));
            }
        }
Пример #2
0
        /// <summary>
        /// Determines whether this object is equal to another object (an <see cref="ArffAttribute"/> with the same name and type).
        /// </summary>
        /// <param name="obj">The object to compare with the current object.</param>
        /// <returns><c>true</c> if the specified object is equal to the current object; otherwise, <c>false</c>.</returns>
        public override bool Equals(object obj)
        {
            ArffAttribute other = obj as ArffAttribute;

            if (other == null)
            {
                return(false);
            }

            return(other.Name == Name && other.Type.Equals(Type));
        }
Пример #3
0
        /// <summary>
        /// Reads relation name and attribute declarations as an <see cref="ArffHeader"/> instance.
        /// </summary>
        /// <returns><see cref="ArffHeader"/> instance with read data.</returns>
        /// <exception cref="ObjectDisposedException"/>
        /// <exception cref="InvalidOperationException"/>
        /// <exception cref="InvalidDataException"/>
        public ArffHeader ReadHeader()
        {
            if (disposed)
            {
                throw new ObjectDisposedException(GetType().FullName);
            }
            if (arffHeader != null)
            {
                throw new InvalidOperationException("The header has already been read by a previous call of ReadHeader.");
            }

            List <ArffAttribute> attributes = new List <ArffAttribute>();

            ReadToken(expectedToken: "@relation", ignoreCase: true, skipEndOfLine: true, endOfLine: false, quoting: false);

            string relationName = ReadToken(endOfLine: false);

            ReadToken(endOfLine: true);

            while (true)
            {
                string token = ReadToken(skipEndOfLine: true, endOfLine: false, quoting: false);

                if (string.Equals(token, "@attribute", StringComparison.OrdinalIgnoreCase))
                {
                    ArffAttribute attribute = ReadAttribute();

                    attributes.Add(attribute);
                }
                else if (string.Equals(token, "@data", StringComparison.OrdinalIgnoreCase))
                {
                    ReadToken(endOfLine: true);
                    break;
                }
                else
                {
                    throw new InvalidDataException($"Unexpected token \"{token}\". Expected \"@attribute\" or \"@data\".");
                }
            }

            if (attributes.Count == 0)
            {
                throw new InvalidDataException("Expected at least one \"@attribute\".");
            }

            arffHeader = new ArffHeader(relationName, attributes);

            return(arffHeader);
        }
Пример #4
0
        /// <summary>
        /// Writes an attribute declaration (@attribute &lt;...&gt;) for the specified <see cref="ArffAttribute"/>.
        /// Must be called after the relation name has been written and before any data instances are written.
        /// </summary>
        /// <param name="attribute">An <see cref="ArffAttribute"/> object representing the attribute.</param>
        /// <exception cref="ObjectDisposedException"/>
        /// <exception cref="ArgumentNullException"/>
        /// <exception cref="ArgumentException"/>
        /// <exception cref="InvalidOperationException"/>
        /// <exception cref="IOException"/>
        public void WriteAttribute(ArffAttribute attribute)
        {
            if (disposed)
            {
                throw new ObjectDisposedException(GetType().FullName);
            }
            if (step != 1 && step != 2)
            {
                throw new InvalidOperationException("All attributes must be written after the relation name and before any instances.");
            }
            if (attribute == null)
            {
                throw new ArgumentNullException(nameof(attribute));
            }

            WriteAttribute(attribute, 0);

            writtenAttributes.Add(attribute);

            step = 2;
        }
Пример #5
0
        private void WriteValue(object value, ArffAttribute attribute, TextWriter textWriter)
        {
            if (value == null)
            {
                textWriter.Write("?");
            }
            else if (value is double doubleValue)
            {
                textWriter.Write(doubleValue.ToString(CultureInfo.InvariantCulture));
            }
            else if (value is string stringValue)
            {
                textWriter.Write(QuoteAndEscape(stringValue));
            }
            else if (value is int || value is Enum)
            {
                // the cast fails if value is an enum with an underlying type other than int
                // but checking the type via Enum.GetUnderlyingType(value.GetType()) == typeof(int)
                // to throw a more helpful exception is probably not worth it
                int nominalValue = (int)value;

                ReadOnlyCollection <string> values = (attribute.Type as ArffNominalAttribute)?.Values;

                if (values == null || nominalValue < 0 || nominalValue >= values.Count)
                {
                    throw new ArgumentException("Instance is incompatible with types of written attributes.", "instance");
                }

                textWriter.Write(QuoteAndEscape(values[nominalValue]));
            }
            else if (value is DateTime dateTimeValue)
            {
                string dateFormat = (attribute.Type as ArffDateAttribute)?.DateFormat;

                if (dateFormat == null)
                {
                    throw new ArgumentException("Instance is incompatible with types of written attributes.", "instance");
                }

                textWriter.Write(QuoteAndEscape(dateTimeValue.ToString(dateFormat, CultureInfo.InvariantCulture)));
            }
            else if (value is object[][] relationalValue)
            {
                ReadOnlyCollection <ArffAttribute> relationalAttributes = (attribute.Type as ArffRelationalAttribute)?.ChildAttributes;

                if (relationalAttributes == null)
                {
                    throw new ArgumentException("Instance is incompatible with types of written attributes.", "instance");
                }

                using (StringWriter stringWriter = new StringWriter())
                {
                    for (int j = 0; j < relationalValue.Length; j++)
                    {
                        if (relationalValue[j].Length != relationalAttributes.Count)
                        {
                            throw new ArgumentException("Instance is incompatible with types of written attributes.", "instance");
                        }

                        // sparse format does not seem to be supported in relational values
                        // instance weights seem to be supported in the format but they cannot be represented with an object[] alone, so we don't support them for now
                        WriteInstanceData(relationalValue[j], false, relationalAttributes, stringWriter);

                        if (j != relationalValue.Length - 1)
                        {
                            stringWriter.WriteLine();
                        }
                    }

                    textWriter.Write(QuoteAndEscape(stringWriter.GetStringBuilder().ToString()));
                }
            }
            else
            {
                throw new ArgumentException("Unsupported data type in instance.", "instance");
            }
        }
Пример #6
0
        private ArffAttribute ReadAttribute()
        {
            string attributeName = ReadToken(endOfLine: false);
            string typeString    = ReadToken(endOfLine: false, quoting: false);

            ArffAttributeType attributeType;

            if (string.Equals(typeString, "numeric", StringComparison.OrdinalIgnoreCase) ||
                string.Equals(typeString, "integer", StringComparison.OrdinalIgnoreCase) ||
                string.Equals(typeString, "real", StringComparison.OrdinalIgnoreCase))
            {
                attributeType = ArffAttributeType.Numeric;
                ReadToken(endOfLine: true);
            }
            else if (string.Equals(typeString, "string", StringComparison.OrdinalIgnoreCase))
            {
                attributeType = ArffAttributeType.String;
                ReadToken(endOfLine: true);
            }
            else if (string.Equals(typeString, "date", StringComparison.OrdinalIgnoreCase))
            {
                string dateFormat = ReadToken();

                if (dateFormat == null)
                {
                    attributeType = ArffAttributeType.Date();
                }
                else
                {
                    attributeType = ArffAttributeType.Date(dateFormat);
                    ReadToken(endOfLine: true);
                }
            }
            else if (typeString == "{")
            {
                List <string> nominalValues = new List <string>();

                while (true)
                {
                    string value = ReadToken(out bool quoted, endOfLine: false);

                    if (!quoted && value == "}")
                    {
                        break;
                    }
                    else if (!quoted && value == ",")
                    {
                        continue;
                    }
                    else
                    {
                        nominalValues.Add(value);
                    }
                }

                attributeType = ArffAttributeType.Nominal(nominalValues);
                ReadToken(endOfLine: true);
            }
            else if (string.Equals(typeString, "relational", StringComparison.OrdinalIgnoreCase))
            {
                ReadToken(endOfLine: true);

                List <ArffAttribute> childAttributes = new List <ArffAttribute>();

                while (true)
                {
                    string token = ReadToken(skipEndOfLine: true, endOfLine: false, quoting: false);

                    if (string.Equals(token, "@attribute", StringComparison.OrdinalIgnoreCase))
                    {
                        ArffAttribute attribute = ReadAttribute();

                        childAttributes.Add(attribute);
                    }
                    else if (string.Equals(token, "@end", StringComparison.OrdinalIgnoreCase))
                    {
                        ReadToken(expectedToken: attributeName, endOfLine: false);
                        ReadToken(endOfLine: true);
                        break;
                    }
                    else
                    {
                        throw new InvalidDataException($"Unexpected token \"{token}\". Expected \"@attribute\" or \"@end\".");
                    }
                }

                attributeType = ArffAttributeType.Relational(childAttributes);
            }
            else
            {
                throw new InvalidDataException($"Unexpected token \"{typeString}\". Expected attribute type.");
            }

            return(new ArffAttribute(attributeName, attributeType));
        }